It's All About Complexity

Complexity, principles for scaling frontend applications, the risks of micro frontends, and more.

Issue 1 /

Hey there,

Welcome to the inaugural edition of Frontend at Scale, your bi-weekly newsletter for all things software design, frontend architecture, and terribly bad puns.

In today’s issue, we’ll talk about complexity, principles for scaling frontend applications, the risks or micro frontends, and… a philosophy book? Let’s dive in.


It's All About Complexity

We should start by addressing the elephant in the room. No, not PHP; the other elephant—complexity.

There’s a famous 2006 paper that speaks very eloquently to the importance of caring about complexity. Here’s a quote from the paper that I think sums things up pretty well:

“Complexity is the root cause of the vast majority of problems with software today. Unreliability, late delivery, lack of security — often even poor performance in large-scale systems can all be seen as deriving ultimately from unmanageable complexity.”

That’s a bit of a spicy take by 2006 standards, but the authors do have a point. When you think about the root cause of frustration in large codebases, whether is tech debt, maintaining legacy code, or making sense of undocumented features, it can often be attributed to some form of complexity.

This immediately brings up another question, though: what exactly is complexity? We know it’s all around us, but how can we single it out? Can we see it? Can we touch it?

Well, no, we can’t touch it. But we can try to describe it. The way I like to think about it is that software complexity is anything that makes the system harder to change, harder to understand, or both. For frontend systems in particular, this could mean an inconsistent folder structure, lack of documentation, disorganized state management, or a combination of all of those.

This is a general definition, of course, so we can’t use it to measure complexity in any way. And if we can’t measure it, how do we know if we’re doing a good job at managing it? To answer this question we need to go one level deeper: if we want to learn how to manage complexity, we have to get better at recognizing its symptoms.

Symptoms of Complexity

Stanford Professor John Ousterhout wrote an entire book about this—A Philosophy of Software Design (we'll come back to this book later in the newsletter.) In his book, Ousterhout describes three ways in which complexity typically manifests itself:

  1. Change Amplification. When seemingly simple changes require updating too many different places in the codebase. You can measure this the next time your PM assigns you a “quick win.” If it turns out it wasn’t so quick after all, that might be a sign that you’re dealing with this symptom.
  2. Cognitive Load. How much information you need to hold in your head in order to understand the module or component you’re working with? In general, high cognitive load is caused by low cohesion (when things that belong together aren’t together), but even minor things like unclear variable names and lack of clarifying comments can add to this.
  3. Unknown Unknowns. Imagine changing a single line of CSS that ends up bringing down the entire production site. That’s an exaggeration (I hope), but I’m sure you can think of similar scenarios. The underlying issue is this: there’s an implicit dependency between two completely different parts of the codebase, but the only way to discover it is when an issue finally occurs. Unknown unknowns are the worst.
A diagram showing the causes and symptoms of complexity
Causes and symptoms of complexity

Paying attention to these symptoms will help you identify when complexity starts to creep into your codebase. Because, make no mistake, complexity is actively conspiring against you, and is very, very sneaky.

Nobody sets out to build a complex system from scratch. But as time passes, more teammates join the team, and more features are added to the product, complexity slowly but surely finds its way in. If you let it grow unchecked, at one point someone in your team will inevitably say the four words engineering managers fear the most:

“We need a rewrite.”

I hear you, frustrated developer. But a full rewrite is a massive endeavor, and almost never the right solution. Also, we don’t want a stressed out manager, do we? Let’s not do that to them. Instead, here are two questions you can ask yourself to help manage or eliminate some of that complexity from your codebase:

  • What’s one thing I can do to make the codebase easier to change in the future, either by me or someone else?
    Take one of your hard-to-work-with modules. Like, that 600 lines-of-code React component that is aways a pain to scroll through. Can you think of a small change you can make to simplify it, even if it’s just by 1%? Maybe there’s a prop or piece of state that you could remove, or maybe you could encapsulate some its side effects using a custom hook. It doesn’t have to be a big refactor—even the tiniest change can have a significant impact.
  • What’s one thing I can do to make the codebase easier to understand from a higher level?
    Design patterns help a lot here, but you could make things easier to understand without even touching the code. You could write a discovery doc or design spec, document an architectural decision, or simply add a comment to explain the purpose of a function or component.

You might be familiar with camping rule of “always leaving the campsite better than you found it.” If you find a mess in the campsite (or codebase, in our case), you clean it up, no matter who made it. No git blame allowed. Asking ourselves these two questions will help us stick to that rule.

But my favorite thing about these questions is that they bring us all the way back to our definition of complexity. When we optimize for change and make the system easier to understand, we’re not just treating the symptoms, we’re attacking complexity directly at its roots.


Principles for Scaling Frontend Application Development

NO_AXIOS in this house

Malte Ubl, CTO at Vercel and former Principal Engineer at Google, gave a talk at React Summit earlier this year sharing his principles for scaling frontend applications. Given that you’re reading a newsletter that is literally called Frontend at Scale, I’m going to go out on a limb here and say that you might find Malte’s talk interesting.

I won’t spoil the talk for you because I hope you give it a watch, but I will share my two favorite principles:

  • Make it easy to delete code. This resonates with one of the strategies we were discussing earlier for managing complexity. Optimizing for change (or deletion) typically results in better, more maintainable designs.
  • Embrace lack of knowledge. We shouldn’t assume that everyone in the team has the same level of knowledge that we have. We should look for opportunities to embed some of that knowledge (for instance, via types, conformance, or linting rules) so that teammates without full-context can still work with the codebase effectively.


Links Worth Checking Out

You might have opinions about Micro Frontends, but you can't deny its effectiveness for managing frontend applications at scale. The benefits of Micro Frontends are not for free, though, and it's important to be aware of its risks and potential drawbacks.

We will talk more about Clean Architecture from a frontend perspective in a future edition of this newsletter, but if you're looking for SvelteKit-specific content, this series of articles is a great resource.

A great visual to help you focus on the most important questions to ask during a code review. Bookmark it for the next time you can't think of anything other than "LGTM!"

Factory Factory Factory

Originally written by Benji Smith

There is a thing such as abstracting too much, and this essay is a perfect example of that. Cry-laugh with me in this painfully real story about a person trying to buy a hammer.

Opinions on React Sever Components are like belly buttons, everyone has one. But not everyone can articulate them as well as Lenz in his latest blogpost.


A Philosophy of Software Design

I know a book with “philosophy” in its name might have caused you to tune out immediately, but in case you’re still reading, I promise this one is worth checking out. It’s a short book (~180 pages), and it’s a relatively light read, free of most of the technical jargon you’ll find in other books of its kind.

If my earlier ramblings about complexity were of any interest to you, this book is the best resource I can recommend to explore the topic further. The code examples are in Java, but the concepts are universal and can be applied in any language.

“Wait a minute. Did you just say Java? I thought this was a frontend newsletter.”

Yes, I did say Java. And yes, I know that Java and JavaScript are no the same thing! (I think.) But the book is more about software design at a high level, so there really isn’t too much code in it. If that still sounds too intimidating, you can do what I do: squint your eyes a little bit, and pretend the book is written in TypeScript.

Just like Out of the Tar Pit, A Philosophy of Software Design is based on the thesis that complexity is the root of all evil. But the book takes things a bit further, saying that managing complexity is the most important part of our jobs as software designers and engineers.

Whether you agree with that statement or not, I’m sure you’ll find the strategies covered in the book useful. A few of my favorites are:

  • Modules Should be Deep. Modules can (and should) pack a lot of functionality, but their interface should remain as simple as possible. This directly applies to components, views, routes, or any of the different types of modules we use on the frontend.
  • Information Hiding and Information Leakage. A good abstraction should be obvious and hide unimportant details. This again applies to functions, components, classes, or even entire libraries.
  • Different Layer, Different Abstraction. If two abstractions provide similar functionality but they operate at different layers of the system, that’s a red flag that could be causing your codebase to be more complex than necessary.
  • Plus, a number of the later chapters in the book cover tips for commenting and documenting your code—a very important but often overlooked component of software design.

That’s all for today, friends! Thank you for making it all the way to the end. If you enjoyed the newsletter, it would mean the world to me if you’d share it with your friends and coworkers. (And if you didn't enjoy it, why not share it with an enemy?)

Do you have any feedback for me? Feel free to reach out on Twitter or reply to this email directly. I appreciate all your comments, good or bad.

Someone forwarded this to you? First of all, tell them how awesome they are, and then consider subscribing to the newsletter to get the next issue right in your inbox.

Have a great week 👋

– Maxi

Is frontend architecture your cup of tea? 🍵

Level up your skills with Frontend at Scale—your friendly software design and architecture newsletter. Subscribe to get the next issue right in your inbox.

    “Maxi's newsletter is a treasure trove of wisdom for software engineers with a growth mindset. Packed with valuable insights on software design and curated resources that keep you at the forefront of the industry, it's simply a must-read. Elevate your game, one issue at a time.”

    Addy Osmani
    Addy Osmani
    Engineering Lead, Google Chrome