Ruminations on Front-end Architecture

Things are generally only as strong as their weakest link, and this holds true for software as well. In the physical world, the structure of a building is clearly articulated by an architect before a developer may get started on its construction, and there’s an evidently good reason for this order of operation. While the concept of architecture in software is nothing new, the average modern front-end has crossed the line where what I call “responsible development” becomes difficult to achieve without it.

The still somewhat nascent realm of front-end architecture as a discipline is likely to take on different forms in practice, but my experience wearing the front-end architect hat on occasion has led me into coming up with a list that encapsulates its core functions, as I see it. This basic set of attributes is aimed at preventing common structural-design problems plaguing the frontend, regardless of size or technology stack.

A front-end architecture should:

  1. Describe the architecture with a semantically sound structural vocabulary.
  2. Implement patterns that enable seamless and predictable scaling.
  3. Set guidelines that maintain the architecture’s integrity.

While these stipulations are not exhaustive, they form a solid foundation that goes a long way towards maintaining the architectural health of a frontend codebase. Let’s cover how each of these expectations might translate into practice.

Describe the architecture

This is a schematic “blueprint” that describes the structure of the codebase and takes care of naming its constituent parts (I like to call it an architecture’s “nodes”). It is also responsible for establishing sound naming conventions that take the guesswork out of the much-dreaded problem of nomenclature for developers.

Some benefits:

  • Management and developers have access to an approachable schema that clearly describes the codebase to help them with planning.
  • Good quality naming assists developers with visual cueing and makes navigating the codebase easier.
  • Developers avoid the time and hassle of dealing with one of the hard problems in computer science.
  • New developers are onboarded more effectively with a high-level overview of how the front-end is structured.

Implement patterns

Asset patterns

A great benefit of being proactive about front-end architecture is the ability to anticipate and provide for the potential of change — and in particular growth — whether minuscule or meteoric. A core function of the front-end architect is to devise patterns that allow the front-end to scale seamlessly and to whatever extent possible, in a predictable manner. Naturally, the particulars of such patterns are dependent on the flavor of the technology stack that the architect is working with.

Let’s take image assets as an example. It typically requires thinking about the various image formats (i.e. GIF, JPG, PNG, WEBP, and SVG), and how to structure vector assets differently from rasters based on how they will be consumed by developers, as well as the project’s tooling. For instance, since different forms of vector sprites (i.e. “view” and “symbol”) would suit different use cases, and to support both in-lining (i.e. as base64) and linking via the “src” attribute, the front-end architect should lay out patterns for each scenario that makes it clear to developers where assets should go to get the desired result.

This kind of asset-management pattern applies to each level of the “asset hierarchy” if you will. For example, if reusability and composability are achieved through the concept of “components” in an imaginary front-end project, then all assets from the top page-level components down to its indivisible constituents such as the markup, styles, scripts and media files can be served by prescribing clear patterns to developers for managing them.

I’ve witnessed “asset sprawl” on a number of occasions, a result of developers not always being clear on where static files should go and proceeding to inadvertently structure the codebase as they go. This tends to result in things scattered all over the place, and traces of everyone’s idiosyncratic way of solving the lack-of-architecture problem is visible across the file system, ready to haunt the next developer trying to find, place or name static asset “x”. It ain’t pretty, and the ensuing technical debt ain’t cheap as it causes everything from bugs, confusion, asset duplication, and a downright unsightly codebase. Don’t underestimate the value of good structural design and the asset patterns that maintain it.

Tooling patterns

Front-end tooling is (anecdotally speaking) integral to front-end architecture, and the architect that knows their way around it can produce a coherent codebase. The tooling should reflect the architecture in DRYness, extensibility, and scalability, regardless of whether your team has decided on Grunt, Gulp, Webpack or NPM and shell scripts.

Whether starting out with a new project or working on a familiar project, tooling is often a pain point for developers. All too often, valuable time is spent trying to fix a build system that is prone to breaking or doing things manually that could have been automated as an additional step in the build pipeline. The front-end architect can play an instrumental role in ensuring that the experience of interacting with the build tooling is optimized and frictionless for developers. The potential for workflow optimization depends on the nature of the project and the stack at hand as it will naturally dictate the terms to the front-end architect. Let’s look at some basic examples next.

Linter rules sets can be strict about formatting and for good reason. However, a more lenient formatting environment is arguably more conducive to faster iteration during the early stages of development. In this case, when a developer temporarily deviates from the formatting rules in a development context, it is done purposefully for the sake of efficiency, and the linter(s) should not impede this effort. A prudent tooling pattern can ensure that lint errors are handled gracefully to prevent them from potentially interrupting the build process while calling out formatting errors during a development cycle. Another approach for this pattern might be to configure the linter with an extensible rule set, thereby being more forgiving about formatting in a development environment, while a stricter configuration takes precedence for a production build.

Modularization is another fair example. Modularizing the build system’s respective concerns would make it easier to maintain and implement new tooling functionality as each part of the build pipeline is scoped to a module that is dedicated to a single function. For instance, your gulpfile.js could define your build tasks, but those tasks are all discretely organized in a “settings” directory and imported as required.

Set guidelines

Once the architecture is described and the respective patterns are in place, a smattering of guidelines is in order to maintain its health, and by extension the health of the front-end. These guidelines should be documented, preferably along with each aspect of the architecture that could be of potential concern to developers.

The following list covers some of the topics for which guidelines should be in place:

  • Templates
  • Styles
  • Scripts
  • Images
  • Rich Media
  • Components
  • Settings
  • Tooling

Setting guidelines for naming conventions and directory structure applies to every item in the list. Beyond that, each concern will require more specific guidelines to account for their differences. Templates and styles might require rules on managing partials, while module management patterns for JavaScript should be detailed. Images would potentially need guidelines accounting for all the available usage options the project and its tooling supports (i.e. sprites, inlining, linking). In the same vein, the types, composition, and hierarchy of the frontend’s components could be codified.

This is by no means an exhaustive list of topics that could be covered. Front-end projects are particularly distinct creatures that will require case-sensitive evaluation to determine the appropriate set of guidelines. Think of this list as a simple springboard for further ideation.

Additional concerns

Whether the role of Front-end Architect is shared, or vested in a single member of the team, it is important for the responsible parties to weigh in on the technology choices that will make up the stack for its potential architectural implications. After some deliberation, you might find that a certain HTML, CSS, or ECMAScript abstraction lends itself better to a preferred architecture than a comparable technology. For instance, the extent to which a language can be modularized will have a direct impact on how much leniency is afforded to your front-end’s structural design.

Build systems should also be carefully considered, particularly by the architect(s). It’s often wise to seek as little tooling for your money as possible, lest your project evolves into an engineering marvel few dare to touch. Are you building a SPA or an MPA? Will your application need HMR? These questions should steer you in the direction of the appropriate choice of tooling for your situation.

Hidden costs

A tasteful user interface built using edge technologies, and yet lacking the appropriate consideration for its architecture is similar to a technologically advanced smart home built on a weak foundation. As it morphs its way into the future of the web, its brittle architecture is likely to make extending, scaling, and maintaining more difficult and time-consuming. Some of its more hidden costs could surface in creating the need for hiring more experienced developers who are able and willing to deal with the complexity created by the lack of solid architecture, thereby increasing the total cost of ownership.

Epilogue

Ideally, open source frontend libraries, frameworks, and platforms will eventually incorporate aspects of sound structural design into their respective projects, where applicable. They can do this under the banner of sensible defaults, and developers will be saved from having to devise their own architectures.

When we get started on a new front-end, or tasked with a major refactor, we’re often eager to get cranking on some code, our fingers hovering all excited and trigger-happy above the keyboard. I hope that I’ve managed to put forth an argument for restraint, to fight the urge for expedient results and first think about the architecture of your front-end. Prioritizing the structural design of your codebase to come before other developmental objectives are likely to keep paying dividends throughout the lifetime of your application.