How to reevaluate your frontend architecture

How to reevaluate your frontend architecture

A process to break the chains of old, and usher in an age of less complexity, greater consensus, and a more functional technical stack.
Part of
Issue 13 May 2020

Frontend

As engineers, we often judge architectural decisions solely by their technical correctness. We can be quick to reach for metrics on performance, consistency, or availability because these are measurable and readily available. However, the most technically ideal solution isn’t always the one that creates the most value for your organization. This age-old temptation to mistake what you want for what you need means that evaluating technical architectures requires careful consideration.

Most content-based websites, from blogs to newspapers to BuzzFeed, primarily serve read-only, static content. From an engineering perspective, getting this content to users is something of a solved problem: server-render the content into HTML and enhance with JavaScript on the client. This is the method we used to build BuzzFeed, and for a long time we felt it was entirely validated by the platform’s stability.

Since BuzzFeed’s first version 13 years ago, the frontend engineering landscape has shifted toward more ambitious JavaScript frameworks such as Backbone, Angular, and, most recently, React. These frameworks focus on the component as the maintainable building block of user interfaces and unlock entirely new developer experience capabilities. However, their reliance on JavaScript opens up a new set of performance and resilience challenges—whether these challenges are worth the effort in exchange for a better developer experience is often debated by performance engineers and framework evangelists.

Proponents argue that focusing on developer experience naturally leads to a better user experience because you can ship new features and react to feedback more quickly. There’s some truth to this, but we should never assume that when the developer experience improves, the user experience automatically follows. On the other hand, focusing purely on user experience or performance is often shortsighted, as the codebase can quickly become esoteric and hard to adapt to product or organizational changes. Balancing these trade-offs is a constant challenge.

Conference talks, blog posts, and Twitter are replete with strong opinions about the correct way to build software. While they may all have independent merit, they frequently come from a specific, idealistic perspective. As engineers, our goal should be to tailor the technical stack to our specific organizational needs. At BuzzFeed, we prioritized performance and user experience, and we were able to deliver on them with simple technology choices. Years later, performance and user experience still figure among our priorities, yet weʼve decided to transition to a more ambitious technical stack. How did we get here—and what made us take this leap?

Buzzfeed.com is no longer just a read-only website serving static content. It evolved to support commenting, interactive quizzes, video and ad distribution, first- and third-party tracking, affiliate partnerships, and A/B testing. What was once a light, bespoke layer of JavaScript had grown to a size that required significant maintenance and structure, as well as a new focus on runtime performance.

Those who avoid adopting a holistic framework inevitably create their own set of abstractions to provide structure. Typically, these abstractions are understood by their creators and those who use them often, but are easily misunderstood or misused by those who don’t. At BuzzFeed, we often found ourselves apologizing for the complexity of our frontend architecture as we explained (and re-explained) it to new engineers. The amount of information required to onboard a new team member should be viewed as an organizational health check, and understanding our frontend architecture was taking up a lot of our overall onboarding time.

Eventually, this led to discontent among our engineers—they were neither learning new skills nor delivering the technical quality they wanted to. Moreover, many had had no say in the current architecture and felt bound by the decisions of a previous era. It was important to address these concerns as we reevaluated our architecture.

Here, I’ll share the steps we followed. If you strip out the specifics, it can serve as a set of guidelines for any technical or architectural decisions your organization may be facing.

Step 1: Legitimize the debate

Engineers expressed their discontent with our frontend architecture in code review and on Slack, and the architecture itself was becoming a scapegoat for unrelated engineering issues. We knew we needed to address the issue head-on by elevating and legitimizing the conversation.

To do so, I wrote a document that openly challenged our current architecture. It contained the reasoning behind the discussion, why the timing was right to reevaluate our framework, and the two theses up for debate:

  1. We don’t need an ambitious framework to build buzzfeed.com. It is primarily a static, content-driven website.

  2. Buzzfeed.com is primarily a static and content-driven website, but it can be built using an ambitious framework.

This centralized the debate and made it actionable. Opinions that had long been expressed only in informal conversations were now out in the open, able to be considered and discussed by everyone in the organization. This signaled a real opportunity for change, and led to thoughtful and productive comments that demonstrated a clear enthusiasm for it.

Step 2: Give ownership back to the engineers

It’s easy for engineering leads to make architecture decisions and delegate implementation. But while that approach can achieve short-term results, it rarely creates a sense of ownership or responsibility among the engineers who maintain the framework. To achieve that sense of ownership, engineers must own the evaluation and decision-making process. It’s leadership’s role to create an opportunity for this to happen.

At BuzzFeed, our solution was to create a frontend architecture working group that would evaluate the technical options and present their recommendation to the organization for review. Working groups are an excellent tool for driving change, opening up new leadership opportunities, and allowing for cross-team collaboration. They tend to be most effective when they’re small, but they should also seek diverse perspectives by inviting subject matter experts when appropriate and publishing the minutes of every meeting for full transparency and timely feedback. These strategies allowed our working group to stay focused while also providing visibility to the broader engineering organization.

Step 3: Choose your evaluation criteria

In order to determine the best architecture or framework for your organization, you’ll first need to formalize the criteria that are the most important to you. Ours fit into five categories:

Performance

We didn’t want a new frontend architecture to reduce our initial render speed. Given that we had evolved beyond static content, we also needed efficient state changes, support for code splitting and lazy initialization, and high Lighthouse scores.

Maintainability

Our biggest maintenance issues arose from using different template syntaxes on the server and client. Any new solution needed to support a single templating syntax and have good documentation with an active community to lean on.

Componentization

Not having truly independent components was another source of ongoing maintenance issues. A new solution had to support single entry point components which were easily composable and able to define their own dependencies.

Testing

The framework needed an established testing process and complementary testing tools.

Accessibility

The framework needed to be able to produce code that would be accessible to users with different physical and technical capabilities.

Step 4: Evaluate your options

Once youʼve defined your criteria, you can generate a matrix to map the strengths of each solution. You may also find that you need some hands-on experience to get a richer perspective. In our case, we ended up building example applications in each of the framework options (React, Preact, Vue, Web Components, Marko, and Svelte) and dissected the results as a group.

This was enough to discount certain options, but the remaining few were hard to differentiate. React was still a contender, and since we used it for our internal tools, we decided to change course and look for areas where React couldn’t satisfy our needs.

Step 5: Get real-world data

Getting real-world performance data can help fill in the gaps when analyzing the problem from a high-level perspective starts to fall short. Depending on the scale and risk of the technical proposal, this can be an important step. The example applications we’d built were too small to use as real-world examples; we wanted to observe the performance characteristics of a representative BuzzFeed application. Our decision carried enough risk that we were willing to invest the extra time and effort, so we decided to rebuild an existing, high-traffic application in a React, Node, and Next stack over the course of a couple of months.

When rebuilding an application, it’s important to be wary of directly comparing it to its predecessor. Software naturally decays over time, and performance is one of the most visible symptoms: It’s far easier to make an application fast than it is to keep it fast, and it’s rarely possible to swap frameworks without affecting the rest of the codebase. The product offering itself is often reconsidered and refined as part of any rewrite.

While we didn’t want to directly compare our legacy application with the rewrite, we did need to validate our proposed architecture. To do so, we set thresholds and expectations. In our case, we wanted to see the new application perform as well as our legacy application on the server side in terms of the number of requests it could serve per second, per instance; perform equally well on initial render speed; and significantly improve our Lighthouse performance scores.

Step 6: Present your proposal

One of our goals was to give engineers ownership of this decision, so presenting a final proposal, rather than imposing a decision, was an important part of the process. We also wanted the broader organization to own the decision, and to feel included in and excited about this new direction.

Our recommendation from the working group to the tech org read:

We propose that future rendering applications are built in Node.js and React. We are confident that these technology choices will unlock many opportunities for building exciting new features and products while maintaining application performance that will match or exceed our existing stack.

We believe this is a great opportunity to use technologies that increase productivity, provide learning opportunities, facilitate quicker onboarding, and help us meet our performance, testing, and accessibility goals.

It can be daunting to propose significant changes to an organization; our working group sought to mitigate the discomfort by being transparent and communicative throughout the process. The group also ensured that leadership had all the context and endorsed the proposal prior to it being shared with the entire organization. There should be no surprises at this stage—the finished proposal should provoke excitement and galvanize the team around the new possibilities at your fingertips.

Conclusion

For me, following this process was a great reminder that idealistic technology choices don’t always match the reality of creating and maintaining consistent software in an organization. Our job as engineers is not just to build websites, it’s to support a business model, contribute to a culture, grow careers, and, above all, create value.

We knew there were multiple solutions that would fit our criteria and be able to deliver a great user experience. As such, the specifics of the final decision were less important than the definition of our goals and having a process we could trust. At BuzzFeed, our process concluded with two major accomplishments: First, we were able to realign our technical stack with our organizational goals and reassess our position on the spectrum of developer experience versus user experience. Second, the introduction of the working group grew new leaders within our organization, returned ownership of the technical architecture to the engineering team, and set the example that you can make a significant impact within BuzzFeed regardless of your title.

I hope you’re able to apply these lessons to your own technical decisions and benefit from a similarly repeatable process for making significant technical and cultural shifts.

About the author

Ian Feather is a software architect at BuzzFeed.

@ianfeather

Artwork by

Manolo Gamboa Naon

manoloide.com

Buy the print edition

Visit the Increment Store to purchase print issues.

Store

Continue Reading

Explore Topics

All Issues