In this article, I cover ten relatively recent software architecture trends that focus mostly on business oriented frontend development. The first trend actually applies not only to frontend development but also to the development of backend software too.
In the rest of this article, I am going to be identifying a lot of open source software which is what the application developer is working with. The trend is that, in the upper environments (such as integration, user acceptance testing, or production), you go with the enterprise upsell variant instead. Typically, the upsell technology can be available in the vendor’s own separate cloud or as an addon in the most popular public cloud vendor’s respective marketplace offerings. Why not use the enterprise version in the lower environments too? Would that not be better in terms of dev/prod parity? Many enterprise upsell offerings are not yet designed for lower environments, especially if they are cloud native. Deving on the enterprise version increases the risk of vendor lock-in as the developers inadvertently start using their proprietary extensions. Vendors that have done well in offering enterprise friendly versions of frontend focused open source frameworks include Kong, Vercel, and Apollo.
With the ubiquity of smartphones, many companies have chosen a mobile first strategy for application development. There are two major platforms, Android and iOS. For a long time, that meant staffing up on two fairly different skill sets for native mobile app development. It is rare to find a developer who has deep knowledge of both Swift (for iOS) and Kotlin (for Android). Recent advancements in cross platform frameworks, such as react native (the new architecture with the fabric renderer and JSI) and flutter, have made cross platform development a viable alternative for many companies that offer business apps that are not designed to be immersive games. With cross platform development, you need to have only one skill set which is similar to web application development. For the most part, you have only one code base for mobile which means developing each feature just once (not including web). You still have to build separately for each platform and go through Google’s and Apple’s workflows to publish to their respective app stores. In general, cross platform development entails much lower staffing and training costs.
Products tend to increase in size over time in order to increase market share by offering more features and capabilities for more value. If your company develops a mobile app, a wep based SPA (single page application), or both, then you end up with a large number of people working on the same monolithic frontend applications. This can result in high ceremony releases which increase operation and maintenance costs, undermine confidence, and decrease feature velocity. A growing trend is to refactor the monolithic frontend applications into MFEs (or micro frontends) with several reusable components that are maintained by separate smaller teams and released separately yet appear to be part of one big application. In the early days of MFEs, iframes and server side includes were the most notable ways of accomplishing this. More recently, companies are adopting an approach known as module federation from a technology known as webpack. This is for web SPAs but a similar technology called repack does the same thing for react native apps. Another approach is to use a web browser technology known as web components or custom HTML elements. Both react and angular support web components. If you want to create web components that do not depend on yet work well within react or angular, then consider using stencil which is a compiler for building web components from high-level code. MFEs promote component level ownership with team autonomy which tries to minimize coupling yet also tries to minimize duplicity. Those two goals conflict with each other and current trends don’t really address that directly. Pay attention to the level of granularity with these components. The story of MFEs has not been completely written yet.
How a web SPA works typically is the web browser downloads a lot of javascript. Once that javascript has been downloaded and parsed, it starts running. The running javascript makes a lot of XHR calls to the server then uses the responses from those calls to create the DOM (or document object model) which then gets rendered by the browser. Only after all of that happens, does the user see anything. That IPL (initial page load) time can get quite long as web SPAs grow in size.
Two techniques used to remedy this latency issue is to use SSR (server side rendering) and code splitting. With SSR, the javascript doesn’t need to get downloaded and parsed on the server because it is already preloaded by earlier requests. The server runs the javascript which makes the XHR calls and what gets downloaded to the web browser is static HTML ready to be rendered. If you are building web SPAs using react, then next.js is the best choice of technology for SSR. With more recent versions which are based on react server components, you get more control over what is rendered on the server and on the client. If the web browser does need to run any javascript, then that script is split into what needs to be run immediately and what can be run later on. The notion of the IPL metric gets refined into another metric called FCP or first contentful paint. Why is FCP a better metric to track than IPL? The user is more willing to tolerate some latency if they can see some interesting content right away. It is a little tricky to combine MFEs with SSR but it can be done.
With web SPAs and mobile apps, the frontend runs on the user’s hardware and makes API calls to the backend where typically the data resides. Those calls need to be hardened in order for the data to be secured against inappropriate or unauthorized access. This is the trend for authn (authentication) and authz (authorization). With authn, the requester is properly identified then checked if that user has sufficient privileges to make this request with authz. The oauth2 standard defines the mechanisms for how to do that and OIDC defines a protocol where all of the different apps use the same SSO (single sign on) app to collect the credentials or other MFA (multi factor authentication) mechanisms used to authn the user. The oauth2 standard is over a decade old but the implementation details have a lot of impact on frontend development to this day.
API calls from the Internet eventually get routed to a specialized service known as a gateway which validates each request based on the accompanying authentication token. Most cloud vendors provide their own offering for this or you can use the open source kong API gateway. Other related OSS of note are keycloak and supertokens. An API gateway can also address other cross cutting concerns such as logging, monitoring, and rate limiting.
Modern frontend development leverages lots of open source components that are freely available in various public registries and repositories. Companies tend to host their own private repositories that mirror the public OSS repositories but also run code scanning tools that look for security vulnerabilities. Most companies use JFrog Artifactory with Xray for this purpose. You need a package manager to download all of the components that your project depends on, and include almost everything in the artifact that eventually gets uploaded to an app store or downloaded to a web browser. For web SPAs and react native mobile apps, the most popular package managers are npm, npx, and yarn. These tools also get used to automate other developer tasks such as code quality and format checkers, test automation, versioning, and building docker images.
Sometimes teams are organized around the components that they are responsible for. That can lead to an environment where one team is responsible for the backend service that is tightly coupled to a frontend app owned by a different team. There is a business trend where companies tend to transition from a single product to a family of products or even a platform. Because of that, many backend services are developed for a wider audience than a single frontend app. As a response to all of that, frontend teams may start developing their own BfF or backends for frontends services. The frontend app calls a BfF service that is specifically designed for their purposes. This simplifies the frontend code. The BfF code which runs on the backend then handles the complexity of calling other backend services that are not specifically designed for their use cases. For that reason, the BfF is coded using a tech stack most familiar to frontend developers. Many BfFs use the Apollo GraphQL server as the framework for that reason. This can result in a proliferation of BfF specific GQL schemas. The Apollo folks saw this as an opportunity for an enterprise upsell by providing the ability to combine these different schemas into a federated supergraph. If you decide to go with a GQL BfF, then you will most probably use the Apollo Server framework. If you decide to go with the supergraph, then you will also need to include either the Apollo gateway or the Apollo router. The router can integrate with what they call managed federation (a.k.a. Apollo GraphOS) which is their enterprise upsell. If you decide to go with a ReSTful BfF, then consider using nest.js which extends express with a full framework and best practices on how to organize the project for enterprise scalability purposes. If you like angular style javascript decorators used in a way that is similar to spring boot annotations in java, then you will most probably like nest.
If you are using micro frontends, federated GQL, or BfFs, then you might be interested in adopting a monorepo approach. In contrast, a normal repo is maintained by a single team and holds the code for building a single deployable artifact (more or less). With micro frontends, you have multiple separately deployable artifacts maintained by different teams with different release schedules but run in the same web SPA. That makes these components tightly coupled which means that often changes to one prompts similar changes to the others. With a BfF, the backend service is tightly coupled with the frontend app. All of the cross team dependencies can cause a lot of developer pain that is somewhat mitigated by a monorepo which holds all of the code for tightly coupled yet separately deployable artifacts from different teams. The build pipeline for the monorepo makes sure that all impacted systems are tested before any changes get deployed to production. For frontend development focused monorepos, the two most popular tools are nx and lerna. Bazel is a monorepo tool that is more focused on backend development but it is very extensible and recent extensions accommodate the needs of frontend teams.
Which brings me to the next architecture trend which includes, but is not limited to, frontend architectures. You may have heard of it as CI/CD because continuous integration is easier to accomplish than continuous deployment. Though not exactly new, I mention this trend here because there is not much value to monorepos without CI/CD. I will skip past the wide variety of online marketecture devoted to this subject and just stick with the nuts-and-bolts specifics of the developer experience itself. In addition to the code that runs when the service runs, developers must also write code that tests it. This can be in the form of unit tests (where the XHR calls are mocked) and integration tests (that build and deploy the app with the changes in it then procedurally emulates a user testing that app). For web SPAs, the unit tests typically use jasmine, jest, or mocha and integration tests typically use selenium or cypress. The react native stack provides its own testing frameworks for unit testing (jest) and integration testing (test renderer). Every time a commit is pushed to a feature branch, this test automation gets run. If the tests fail, then the feature branch cannot be merged to the main branch. The unit tests run first because they are quicker. The integration tests get run only once all the unit tests have passed. When a feature branch gets merged to the main branch, then a new production intended release gets built and deployed. There are variations of this workflow which may optionally run more extensive test suites such as load tests or deploy to various other upper environments too. In the old days, the mechanism for doing all of this was included as a pipeline configuration that gets run by an open source tool called Jenkins. Many developer shops are migrating from Jenkins to GitHub Actions as the primary means of enforcing CI/CD.
Cross platform or web SPA developers are most likely coding in either javascript or typescript. Typescript gets transpiled to javascript at build time. Javascript code that runs on user hardware runs either in a javascript engine called Hermes or whatever engine that the user’s modern web browser has (V8, Chakra, JavaScriptCore, SpiderMonkey). Javascript code that runs on the backend most likely runs in node.js which incorporates the headless chrome V8 engine. The Apollo server, nest, and server side next also run in node. For various logistical reasons, developers may need to build components based on different versions of node which means they need to have installed and be able to easily switch between different versions of node as they work on different software projects. The two most common tools for doing that easily are nvm and n.
From cross platform to GQL federation to micro frontends, these ten trends tell the story of what modern business focused frontend development is currently struggling with. There is a proliferation of everything and the logistics issues that come with this kind of abundance can be overwhelming. The role of the architect is to manage, or at least obscure, the unintended complexity that comes with the territory. These trends document popular attempts to mitigate these issues circa 2023.