Hey friends 👋 It was my birthday yesterday! I had a great time doing my favorite thing in the world, which is hanging out with my family and eating lots of unhealthy but delicious food. I spent some time writing today’s newsletter as well, which I also enjoy very much (not as much as the food, though.)
What’s that? You’d like to give me a birthday present? That’s so kind of you, and it’s absolutely not necessary… but if you insist, the best gift you can give me is to share Frontend at Scale with someone you think might enjoy it, whether it’s by forwarding this email to them or sharing it in social media. It’s the best way to support the newsletter, and I truly appreciate it from the bottom of my heart.
Today we’ll talk about decision-making, woodworking, analysis paralysis, and what Jeff Bezos has to say about choosing JavaScript libraries. Yes, it looks like this article is all over the place but I promise it makes some sense. I think. Let’s dive in.
SOFTWARE DESIGN
Measure Once, Cut Twice
I love watching woodworking videos on YouTube. There’s something incredibly satisfying about watching skilled woodworkers build these amazing designs in their beautiful workshops (that are suspiciously free of any sawdust, by the way.) I could watch them for hours.
I’m also a big fan of a piece of advice all of these master artisans give: “measure twice, cut once.” This is great advice, and not just for cutting wood—it can save us a lot of pain any time we’re about to do something that is easy to do but hard to undo.
But software is different, and trying to apply this piece of advice to planning, designing, and writing applications can sometimes do more harm than good.
Measuring twice (i.e. considering all possible options and coming up with the perfect design or architecture before writing a single line of code) is not the most effective way to build software. That’s the waterfall approach, and we know it doesn’t work.
Instead, we can use the flexibility of software to our advantage and flip the woodworker’s advice on its head. We can measure once, decide quickly, and cut as many times as needed.
Analysis Paralysis
You might be familiar with Jeff Bezos’ decision-making framework, which breaks down decisions into two types:
- Type 1 decisions, which are irreversible and need to be considered carefully, and
- Type 2 decisions, which are reversible and should be acted upon quickly.
According to Bezos, the problem comes when we confuse the two. Here’s how he talks about the dangers of treating a Type 2 decision as if it were an irreversible Type 1:
"As organizations get larger, there seems to be a tendency to use the heavy-weight Type 1 decision-making process on most decisions, including many Type 2 decisions. The end result of this is slowness, unthoughtful risk aversion, failure to experiment sufficiently, and consequently diminished invention. We’ll have to figure out how to fight that tendency."
I recently fell into this trap myself. As I was planning and researching the foundation for a new project, I got caught up in trying to come up with the perfect folder structure and the right set of libraries to include in our app template.
Here are some of the questions that went through my head as I was working on this project:
“Should we use React Query or SWR for async state management? Should we group components by feature or by function? Should we use Domain Driven Design or Clean Architecture to define the project structure? Maybe I need to do some more research? What type of research? Should I read a book or take a course? What should I have for dinner today? Hey Siri, where is the nearest Taco Bell? …”
Ok, I went a bit off-topic at the end, but you get the idea. It was a classic case of analysis paralysis, which is not uncommon given how many options software developers have to accomplish the same thing. But this inability to make decisions was taking too long, and more importantly, it was blocking my team from making any progress.
After taking a step back, I realized that a lot of these questions, like most questions in software design, were Type 2 decisions. Were they important? Sure. But were they irreversible? Not really.
We can always revisit the folder structure if it turns out it doesn’t scale to accommodate the needs of every team. If React Query ends up being too painful to work with, we could explore alternatives if and when that problem comes. If there isn’t a Taco Bell nearby… well, that might actually be a real problem.
Now, I’m not advocating for doing no planning at all (it’s “measure once”, not “measure zero times”), but when you’re facing a bunch of Type 2 decisions like these ones, coming up with an initial plan and implementing it quickly is much more beneficial than agonizing over the perfect design for weeks or months.
Measure Twice, Sometimes
Some decisions in software design are truly irreversible. Maybe not impossibly irreversible (like sending an email to thousands of customers by mistake, not that it ever happened to me of course), but at least extremely costly.
Deciding to adopt micro-frontends, or microservices, or migrate your one-million-lines-of-code app from Svelte to Angular wouldn’t be entirely irreversible, but it would make it very hard to change your mind later on.
In these cases, it’s important to consider things more carefully—because as Bezos also warns us, the risks of treating a Type 1 decision too lightly can have disastrous consequences.
Here are a couple of things that can help in these situations:
Prototyping
My favorite way to handle risky and potentially irreversible decisions is to lean heavily on prototyping and proof of concepts. Prototypes are a fantastic tool for test-driving designs before committing to a final implementation, and they almost always result in an improved solution.
You can even take this a step further and turn your prototype into an experimental feature that you actually ship to production. Do you think React Server Components are the right fit for your project? Migrate a small corner of your app to use RSC, and put it behind a feature flag. An actual implementation will teach you a lot more than reading every blog post ever written about them.
It’s OK for prototypes to fail. In fact, when it comes to irreversible decisions, a failed prototype should be considered a big success (it saved you from implementing the wrong solution.) The important thing is that you learn something from them regardless of the outcome. A good prototype not only allows you to fail fast but also to fail forward into the right solution.
Design it Twice
This is one of John Ousterhout's principles in his book A Philosophy of Software Design, and pretty much a literal adaptation of the “measure twice” advice:
“Designing software is hard, so it’s unlikely that your first thoughts about how to structure a module or system will produce the best design. You’ll end up with a much better result if you consider multiple options for each major design decision: design it twice.”
This isn’t something you should always do, but it’s a good strategy to keep in mind when dealing with risky Type 1 situations.
Yes, designing a system or module twice will take additional time that you probably don’t have, but if an extra day or two today can save you weeks or months of pain in the future, it’s probably worth considering.
Maximizing vs. Satisficing
In the 1940s and 50s, Nobel Prize and Turing Award-winner Herbert Simon published a series of papers on the topic of “rational choice” that heavily influenced the way we talk about making decisions.
If we could go back in time and ask Dr. Simon for his thoughts on reversible vs. irreversible decisions, he’d probably tell us… “who are you and what are you doing in my house? Also, what are those ridiculous clothes you’re wearing? Are those rubber shoes with holes in them?”
But right after that, he might tell us about his research, the difference between satisficing and maximizing, and how we naturally have an inclination for one or the other. Jason Cohen wrote a great article about this that defines these terms very clearly:
“Maximizing” means finding the best solution. It requires exploration and analysis to ensure “the best” option hasn’t been overlooked, and that we have confidence in our evaluation of the options.​“Satisficing” means picking the first or easiest or least-expensive option that satisfies the requirements. Preferring a faster decision to the best decision. It means not getting paralyzed by the pursuit of “perfect,” but as a result, rarely results in the very best solution.
I'm a natural maximizer—I like to have a perfect plan and consider every use case before making a decision, but this often leads me to treat any decision as it if were an irreversible one. I need to make a conscious effort to balance things out.
You might be the opposite, a natural satisficer, which is great for making most decisions in software development but can lead to underestimating the big risky ones. You might need to balance things out the other way.
Regardless of your natural inclination, the important thing is to choose when to maximize and when to satisfice. Consider if decisions are truly irreversible and plan accordingly. Is it a Type 1 decision, or a Type 2? Do you really need to measure twice, or is once enough?
Some decisions truly require that we double and triple-check every angle before committing to them. But others, especially in software design, are better made quickly than perfectly—or not at all.
ARCHITECTURE SNACKS
Links Worth Checking Out
đź“• READ
- Speaking of making decisions, Jack Lindamood wrote about every decision he made while running infrastructure at a startup, and whether he stands by them or regrets them.
- Are Frontend developers finally getting the recognition they deserve? Kate Holterhoff certainly thinks so, and she explains us why in this great article on the RedMonk blog.
- The React team wrote about a ton of really cool features coming to the framework soon, including the React Compiler, Actions, and support for Web Components in React 19.
- Sunil Pai wrote an insightful article on how PartyKit, his real-time platform, allows us to write serverless functions that are stateful.
- Are you a fan of Astro and newsletters? Then I highly recommend subscribing to Astro Weekly by Nathan Lawson. It’s fun, short, sweet, and my favorite way to keep up with everything that happens in the Astro universe.
​
🎥 WATCH
​So You Think You Know Git? by Scott Chacon
To quote my friend Justin, my answer to the talk’s title is “No I do not.” But if you use Git in the CLI and, like me, have been using the same three or four commands exclusively for the past fifteen years, you’ll find a ton of really great tips in this talk. Did you know that Git is constantly shipping new features and that the project averages nine commits per day? That’s amazing. Who would have thought? Not me.
​
🎧 LISTEN
​Architectural cloud design patterns with Keith "Danger" Casey
This is a really fun listen on the PodRocket podcast, especially if you like to play around with the infrastructure side of things. It’s a short and interesting conversation where you can learn a lot about design patterns and strategies you might have heard of but perhaps never implemented—things like pub-sub, lift and shift, circuit breakers, blue/green deployments, and the strangler pattern. Also, how cool is the nickname “Danger”, huh?
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?)
Did someone forward 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.
I read and reply to all of your comments. Feel free to reach out on Twitter or reply to this email directly with any feedback or questions.
Have a great week đź‘‹
– Maxi