The evolution of Frontend Development

Photo by Alessio Ferretti / Unsplash

Frontend development has lived an enormous evolution involving many changes over recent years. We have seen many new frameworks that come month after month, incorporating, relatively, new ideas, patterns, and development paradigms. At the same time, there has been an active evolution of the web standards that rule over the World Wide Web and the JavaScript programming language, which actively adds new features to help us, programmers, write better web applications.


Nevertheless, Web Development is still a young software engineering area compared to others like desktop applications or operating systems, which are several decades ahead in terms of maturity and legacy. It is so, an engineering area that is actively evolving, incorporating ideas from this large legacy of architecture, and creating or adapting new ones for the unique characteristics of web applications running on a web browser.

Article series

  1. The evolution of Frontend Development
  2. The rise of Single Page Applications
  3. What Front-end development really means?
  4. JavaScript Engine and the Event Loop
  5. JavaScript Programming Paradigms


In this post, you'll learn what is the meaning of Frontend Architecture through the use of patterns and practices that have emerged over the decades since the birth of the first web applications, understood as software programs that run in a web browser. I describe what are the main concerns that different architecture styles solve when it comes to the specific challenges of designing applications built around ubiquitous standard interfaces as the DOM and Web APIs.


I'll begin this post by defining what is understood as Frontend Architecture in terms of software architecture concepts. Then, I describe the historical evolution of frontend architecture to get a context of the particularities and reasons about some of the patterns and practices that have emerged to solve some scalability challenges. Let's start by visiting the concept of frontend software architecture.

What is Frontend Architecture?

In general terms, Software Architect is a slippery concept, that is hard to define in a granular level of detail, moreover, its meaning changes over time, according to the processes, methodologies, and patterns used to build software, that is in constant evolution.


Although, there are some formal attempts to define software architecture, like the definition from the Software Engineering Institute,

The software architecture of a system represents the design decisions related to overall system structure and behavior. Architecture helps stakeholders understand and analyze how the system will achieve essential qualities such as modifiability, availability, and security.

That's a general and high-level definition, but it gives us a couple of key ideas: first is that architecture is about the design decisions, which is no other thing than the rules and constraints to define how a system should be constructed and what is its desired behavior. Second is the essential qualities that define the success criteria of our system (quality, availability, scalability, and others, often referred to as the -illities), which are generally orthogonal to the functionality of the system.

Architectural styles

Another important concept is the architectural style. Chris Richardson described this in the book Microservices Patterns as a limited set of elements(components) and relations(connectors) from which we can define a view of the application's architecture (using the 4+1 architecture view model, a view is either: logical, process or physical). Usually, an application uses a combination of architectural styles, for example, later in this chapter we'll see how Single Page Application structure the implementation of a web app similar to a desktop application that runs in the web browser and also behaves similarly.

We have defined what is software architecture, but the question is still open, what is frontend software architecture?. For the goals and scope of this book, we'll define it as the software architecture that is scoped or limited to the context of systems that run in a browser run-time environment and its interactions aka communication with external systems. Thus, not including server applications like web services, batch or event processing software, or data consuming applications that directly talk to databases. Instead, we focus on the frontend part of such kinds of applications when there is some inherent component, such as the case of micro-frontends, an intersection of micro-services (backend), and frontend elements.

Architecture patterns

Lastly, we have the architecture patterns, defined as a general and reusable solution to common problems in software architecture, within a given context. A similar concept is the design patterns, popularized in the classic book Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional, 1994), which have a narrower scope, but for the sake of simplicity and the goals of this book, we'll use both concepts interchangeably.

I continue this section with a brief overview of the historical evolution of frontend architecture and the different styles and patterns, that several web development projects have created to tackle some architecture challenges and get a good understanding of how they work (and more importantly, why) and when they work better, depending on the trade-offs of the specific problem to solve.

The fast evolution of Frontend development

In the beginnings of Frontend development, software engineers had fewer tools than the plethora of frameworks and libraries available today. They only had the Document Object Model or DOM and a handful of Web APIs, to manipulate the content tree that represents a single web page.

If a software engineer was asked to build a simple web page to display a list of dog breeds, and for each breed selected to show a mosaic of related dog pictures, like the layout exemplified in the figure below, the options were far less flexible and low-level, that the tools we have nowadays with modern web development frameworks.

Simple web page showing a list of dog breeds and a set of pictures of the selected breed.

In the beginning, there was HTML, CSS, and JavaScript

Let's go back in time to the early 2000s. The way a software engineer wrote a web page was using plain JavaScript (or more formally ECMAScript version 3), in combination with Cascading Style Sheets (CSS version 2) and the Web APIs to manipulate the contents of the DOM, as shown in an oversimplified model in next diagram (bellow), where there are two distinct layers, the application code a developer writes, on the top, and the Web APIs and the DOM on the bottom, which only provide a low level of abstraction functionality to the application code.

Simplified model of the levels of abstraction present on the code of a web application.

The code snippet below shows a possible implementation of the web application to display a list of dog breeds in plain HTML. Its flow is straightforward, from top to bottom there is the title of the page, the dropdown button, the dropdown list, and the picture mosaic of dog pictures.

<html>
  <body> 
    <h3>Dog Breeds</h3> 								
    <button id="dd-button" class="dd-button"> 			
      Select a breed
    </button>
    <div id="dropdown" class="dd-content"></div> 		
    <div id="picture-wrapper" class="pictures"></div> 	
    <script src="main.js"></script> <5>
  </body>
</html>

The script referenced (`main.js`) is responsible among other things, to fetch the list of dogs from a remote API (Dog API) and dynamically modify the list. The next snipper shows a fragment of this script, where bootstrap()  fetches the API by calling fetchJson(), and in turn, populate the dropdown list using function appendBreedToDropDown().

document.body.onload = bootstrap;

function bootstrap() {
  var dropdown = document.getElementById(’dropdown’);
  fetchJson(BREED_URLS.list(), appendBreedToDropDown(dropdown));
}

function appendBreedToDropDown(dropdown) {
  return function (data) {
    Object.keys(data.message).forEach((name) => {
      var anchor = document.createElement(’A’);
      anchor.textContent = name;
      anchor.onclick = () => onSelectBreed(name);
      dropdown.appendChild(anchor);
    });
  }
}

function fetchJson(url, callback) {
  var xmlhttp = new XMLHttpRequest();
  xmlhttp.onreadystatechange = function () {
    if (this.readyState == 4 && this.status == 200) {
      var myArr = JSON.parse(this.responseText);
      callback(myArr);
    }
  };
  xmlhttp.open("GET", url, true);
  xmlhttp.send();
}

We can easily spot that using vanilla JavaScript has several downsides: a developer needs to worry about several concerns in the same script, including modifying the DOM to add dynamic behavior, fetching the remote API, and handle the side effects (A side effect is a function that modifies state variables outside of its local environment), and maintaining the application state. Separation of concerns is key in achieving maintainability and scalability in the long run.

Another problem at the time was the verbosity in the many steps to get simple tasks done, like with the function fetchJson, which makes an HTTP get request and retrieves a JSON object using a promise taking more than seven lines of code.

Then it was JQuery

Developers soon realized the need of adding an abstraction layer between the low-level APIs and the application code, to reduce the number of steps required to do DOM manipulations. Improving that way the readability and solving another common problem at the time, browsers not following the DOM standard in their implementations, causing the same code in a browser to not work the same way in another. Thus, an abstraction layer could handle these differences internally, providing a more transparent and simplified API to the developer, as illustrated in the next diagram.

Simplified model of the levels of abstraction present on the code of a web application, with an abstraction layer over Web APIs

With JQuery, a function like appendBreedToDropDown(), can be rewritten more concisely as follows,

function appendBreedToDropDown($dropdown) {
  return (data) =>
  Object.keys(data.message).forEach((name) => {
    $dropdown.append(
      $("<a></a>")
       .text(name)
        .on("click", () => onSelectBreed(name))
    );
  });
}

And the fetchJson() is reduced to a single line of code,

function fetchJson(url, callback) {
  $.ajax({ url, success: callback });
}

JQuery adoption in Front-end development was a step ahead in terms of readability, maintainability, and browser compatibility. However, there were still some problems to achieve scale in large web applications; one of those problems is the imperative nature of working with the DOM using queries based on selectors. Every time a developer needed to add more HTML elements or modify existing ones, she or he also needed to make the script companion’s corresponding changes. Working in this fashion is easy to miss the right selectors and HTML structure, not even mentioning how a developer could have a functional test for this web application.

An then JavaScript Frameworks were created

In a similar fashion as the development in other areas as desktop or server applications, software engineers realized they wanted to build applications with reusable libraries or complete frameworks that provide functionalities according to a design philosophy in mind (architecture style), to solve a specific set of design problems in the wide variety of challenges a project presents, depending on functional and non-functional requirements.

Simplified model of the levels of abstraction present on the code of a web application with a framework layer over the Web APIs and the DOM

Development of web applications nowadays works in a scheme very much alike as the above diagram, where the Frontends engineers take as a base an existing set of libraries of frameworks that provide a higher and more rich functionality to manipulate the DOM, interact with Web APIs (among other things) than JQuery could do.

Some of the early projects to have success in terms of adoption were Angularjs (2010), knockout (2010), ember (2011), React (2013). Followed by other popular frameworks in recent years like Vue (2014), Angular 2+ (2016), and svelte (2016) to cite some. Every one of these libraries tries to solve different design problems with a different paradigm or design pattern in mind.

In the next post, we classify architecture patterns according to a problem area, matching the contents we will study during this series.