Hey friends 👋 I hope you had a fantastic last couple of weeks. Mine were quite busy, but overall pretty good. I’ve been hard at work getting the Fundamentals of Frontend Architecture workshop ready for prime time, and even though the project is running a bit behind schedule (sorry about that!), it's still very much underway. I’ll have more exciting news to share about it in the next issue of the newsletter.
Today’s issue is packed with goodies. We'll talk about how to get unstuck when dealing with very hard problems, the performance impact of large DOM element trees, keeping up with everything that’s going on in the JavaScript ecosystem, and much more.
Let’s dive in!
SOFTWARE DESIGN
A Recipe for Getting Unstuck
Have you ever been stuck with a problem for a really long time?
And I don’t mean stuck for a few hours or a few days, like when you have to debug why your app only crashes for users in Australia using Firefox on Android 12, or why you can’t send emails further than 500 miles.
I’m talking about those problems that are so daunting and complicated that can keep us stuck for weeks, months, or even years.
I was dealing with one of these problems at work until very recently. A few years ago, we started noticing that our frontend codebase was struggling to keep up with the needs of our growing team and product. Builds were taking longer to finish, cycle times were slowing down, and our team was frequently stepping on each other’s toes when we shipped code to production.
It was becoming increasingly common for a change in one part of the product to have unexpected consequences on a completely different corner of the app—and needless to say, the team that owned that corner of the app wasn’t thrilled about the breaking changes.
After thinking hard about these challenges, we came to the conclusion that adopting a micro-frontend architecture could help us deal with some of our biggest pains.
By breaking down the monolithic codebase into smaller, individually deployable apps, we could eliminate unexpected breaking changes and give folks more confidence in their releases. Builds would be faster since we would be working with smaller applications, and teams would have more autonomy over their apps, allowing them to move much faster.
So we had a solution in mind and a vision for the future. All we had to do was come up with a plan and implement it. Sounds easy, right?
Unfortunately, we quickly realized that adopting micro-frontends turned out to be a much larger effort than we originally anticipated: there were lots of moving pieces to coordinate, dependencies with multiple teams, and, like with every large endeavor, tons of unknown unknowns.
Of course, there were also known unknowns, which brought up questions such as: How would this migration impact performance? How are we going to share code? How are we going to communicate across micro-frontends? And so on.
It was certainly not an impossible problem, but it was so hard of a challenge that we just couldn't muster the time and energy a project like this required. So we kept going with the status quo—for years.
Micro-frontends became a sort of utopian vision for our team. It was the perfect solution that would fix all of our problems, but we just couldn't move forward with it.
In other words, we were stuck. But what we didn’t realize at the time was that this perfect solution—with all of its promises for a better future—was precisely what was holding us in place.
Beware of Anchor Problems
In their excellent book Designing Your Life*, authors Dave Evans and Bill Burnett have a name for the type of problem we were facing—anchor problems. They named it that way because, just like a physical anchor, these problems hold us in place and prevent us from moving anywhere near a solution.
Luckily for us, Dave and Bill also give us a recipe for getting unstuck whenever we’re facing an anchor problem, which consists of just two steps: reframing and prototyping.
"When you’re stuck with an anchor problem, try reframing the challenge as an exploration of possibilities (instead of trying to solve your huge problem in one miraculous leap), then decide to try a series of small, safe prototypes of the change you’d like to see happen. It should result in getting unstuck and finding a more creative approach to your problem."
When we take a step back to reframe our challenge we can uncover what the actual underlying problem is. For our team, the problem wasn’t really “how can we adopt micro-frontends?”. Micro-frontends were just one of the possible solutions to our real problem, which was that our frontend codebase wasn’t scaling to the needs of our team.
By reframing the problem, we’ll discover the real questions we should be asking ourselves. In our case, these questions were along the lines of
- How can we make our codebase more scalable?
- How can we give teams more confidence in their releases? and
- How can the team move faster and with less friction?
Now we have something to work with. There are lots of ways to answer those questions—many of which are not nearly as daunting as migrating our codebase to micro-frontends.
Once we’ve come up with the right questions and explored a few ways to answer them, the next step is to prototype a solution and learn as much as we can from it.
In our team, our first prototype involved bringing two of our most frequently-changed codebases together into a monorepo. Thanks to that prototype, we found that a monorepo was a great solution to some of our challenges, specifically the need to move faster and with less friction—we no longer had to manage releases for each individual codebase, and the feedback cycle of making a change to a package and seeing it live on the application was reduced significantly.
We also found that Turborepo’s remote caching feature sped up our CI builds considerably. And thanks to some extra tooling and guardrails we put in place, we now have much better visibility into the impact of our releases.
Of course, things are far from perfect, and we still have a long way to go. Also, the fact that we didn’t go with micro-frontends this time doesn’t mean that they’re completely off the table. We might still consider adopting them at some point in the future, but now we don’t have the immediate need to do so.
And despite things still not being perfect, we know that if we hadn’t reframed our anchor problem to explore alternative solutions, we’d still be stuck in the sample place we were for the last couple of years.
How about you, dear reader? What anchor problems are holding you back?
Maybe you’re avoiding working on performance improvements because you’ve fixated on a specific solution that is just too hard—like migrating your entire codebase to Solid.js, or Qwik, or whatever the latest blazingly fast JavaScript framework is these days.
Or maybe you’ve stopped making tiny improvements to your app because you’ve planned to do a full rewrite anyway… but just thinking about the rewrite makes your head start spinning.
Whatever the case may be, if the problem that you’re dealing with is so challenging that keeps you from taking any action towards a solution, you might be dealing with an anchor problem. If that’s the case, try reframing it and prototyping. They might not solve your problem, but they will likely help you get unstuck.
I’ll leave you with some parting words of wisdom from Bill and Dave:
“Don’t make a doable problem into an anchor problem by wedding yourself irretrievably to a solution that just isn’t working. Reframe the solution to some other possibilities, prototype those ideas (take some test hikes), and get yourself unstuck.Anchor problems keep us stuck because we can only see one solution—the one we already have that doesn’t work. Anchor problems are not only about our current, failed approach. They are really about the fear that, no matter what else we try, that won’t work either, and then we’ll have to admit that we’re permanently stuck—meaning we’re screwed—and we’d rather be stuck than screwed.”
* If you’re thinking, “Why on Earth is Maxi quoting a book about design-thinking on a frontend development newsletter?”, well, first of all that’s a very good question. And second of all, you’d be surprised by how many lessons from the world of design-thinking apply perfectly to our jobs as software engineers.
After all, software design is as much about software as it is about design, and hopefully today’s essay serves as a small proof of that. Anyway, Designing Your Life is a pretty good book in case you’re feeling generally stuck—or if you’re in the mood for something a little different :)
đź“ť FROM THE BLOG
How Deep is Your DOM?
If you use Lighthouse to measure your site's performance, you might be familiar with the Avoid excessive DOM size message, which warns against the dangers of... well, large DOM sizes. But there's another interesting aspect of this warning: not only the size of your DOM can impact your site's performance, but also its depth.
Last week, I went down the performance rabbit hole to find out how DOM depth can slow down the rendering of our sites, and I came out the other way with a quick experiment to share with you. Read all about it in my latest article on the Frontend at Scale blog 🧪
​
Read on the blog |
ARCHITECTURE SNACKS
Links Worth Checking Out
đź“• READ
- Anthony Shew is writing Monorepo Maestros, a comprehensive guide with tips, recipes, and everything you need to know about working with monorepos.
- My teammate Jakub wrote a great article on the Help Scout blog about a cool strategy our team uses to speed up our React testing times.
- Looking for a long read to enjoy with your morning coffee? Look no further than this excellent deep-dive by Juntao Qiu on data fetching strategies for single-page applications.
- And if you’re in the mood for a lighter read, the Google Testing Blog published a nice short article on the dangers of prematurely DRY’ing your code.
- Tony Messias and Omar Rida shared a few tips and tricks on how to build optimistic UIs with Livewire and Alpine.
​
🎥 WATCH
​Navigating the JavaScript framework ecosystem​
Katie Hempenius and Addy Osmani gave a fantastic presentation at Google I/O last month showing us the latest and greatest developments in the JavaScript ecosystem. They give us an overview of the amazing features that most frameworks are shipping with these days, as well as an update on the latest developments in React, Remix, Next.js, Vue, Nuxt, Angular, Svelte, and Astro. Phew, that was a lot! If you’re struggling to keep up with everything that’s been going on in the JavaScript world lately (and who isn't, really?), this talk is an excellent way to catch up.
​
🎧 LISTEN
​What Does “Full Stack” Mean? w/ Taylor Otwell and Ryan Florence
Believe it or not, people on the internet are still arguing about Laravel vs full-stack JavaScript frameworks, but the good news is that the discussions are getting a lot more interesting. I really enjoyed this episode of the How About Tomorrow? podcast with two people who not only have opinions on this matter but also know what they’re talking about—Taylor (creator of Laravel) and Ryan (co-creator of Remix.) There are a lot of nuanced opinions, lots of admiration and respect for each other’s work, and even a few spicy takes here and there to keep things interesting. If you want to catch up with this topic but want to skip all the drama, I highly recommend giving this one a listen.
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