Refactoring with the Squint Test

Refactoring, islands architecture, component libraries, and a book about functional programming that doesn't require a Ph.D. in math.

Issue 6 /

Hey there,

We're switching things up a little bit this week. Instead of the usual wall-of-text long essay you're used to, today you'll get a shorter one, plus a sneak peek at the latest article you can read on the blog.

Did I say blog? That's right, there is a blog now! It has exactly one article at the moment, but expect to find more in the upcoming weeks.

So today, you're getting both a shorter email and more content. That's a pretty good deal, isn't it? This format might become more common as I start posting longer-form articles on the blog, so please let me know what you think about it :)

This week, we'll talk about refactoring, islands architecture, component libraries, and a book about functional programming that doesn't require a Ph.D. in math. Let's do this!

SOFTWARE DESIGN

Refactoring with the Squint Test

Imagine you're trying to make a change to your codebase. Something simple, like adding a new item to a dropdown menu or a couple of new fields to an existing form. Easy-peasy.

You're not too familiar with this part of the codebase, so you look around for where this component is getting its items from. When you finally find it, one of two things could happen:

It could be that the change is actually as easy as you thought it would be. Maybe all you have to do is add a new item to a pre-existing array, copy and paste a unit test, and take the rest of the day off. If the change is really that easy, it's usually a sign that this particular piece of code is using the right abstraction.

Or it could be that the change is not easy at all. In fact, you might come across a piece of code so messy and fragile that you fear looking at it too closely could bring the entire app down. More often than you'd like, this is what you'll find, and it's a clear indication that the code could use some refactoring.

Refactoring is a broad topic, and there's typically a lot of ceremony around it. But today, I want to talk about none of that. Today, I want to talk about a refactoring method that you can use every day right after you finish writing a piece of code—what Sandi Metz calls the squint test.

Here's how to do it: squint your eyes, take a step back, and look at the code. The goal of the squint test is to lose focus on the actual code and look for two things—changes in shape, and changes in color.

The squint test on a particularly messy function

Changes in shape mean nested conditionals or loops, which will always be hard to reason about. Changes in color (typically represented by magic strings and magic numbers) mean the code is dealing with different levels of abstraction, so the story it tells is going to be hard to follow.

In her talk All the Little Things, Sandi shows an example of the squint test in action on the popular refactoring kata The Gilded Rose. This is one of my favorite programming talks of all time and a true refactoring masterclass in only 38 minutes. Definitely give it a watch right after you finish reading your favorite newsletter.

The squint test doesn't tell us how to refactor a piece of code, but it's useful for identifying code that could benefit from changing. It's also the easiest way to make sure your abstractions follow the Single-Responsibility Principle. It's not a bulletproof scientific method (in case the name of the test didn't give that away), but it's a pretty good rule of thumb.

So, next time you finish writing a class or function, take a few seconds to squint your eyes and lean back on your chair. If your code fails the squint test, it could mean that you're dealing with the wrong abstraction or that your function might be hard for others to understand—even if it makes complete sense to you.

FROM THE BLOG 📝

Sharing State with Islands Architecture

Islands Architecture is one of my favorite rendering patterns for building websites today. It's performant, scales really well, and reminds me of my lifelong dream: move to Hawaii and eat nothing but Loco Moco and Malasadas for the rest of my days (which won't be too many with that diet.)

But Islands Architecture comes with a tradeoff: communicating between interactive components is not as straightforward as in other rendering patterns.

Thankfully, there are several ways to solve this challenge, and I talk about some of the most popular ones in this week's blog post.

WATCH LIST

Creating Reusable Components… That Are Actually Reusable

Cory House and the 50 tiny decisions involved in building a component library

Cory House gave a great talk at React Rally 2023, sharing his learnings on building reusable component libraries—which is much harder than it might seem on the surface.

Not only are there lots of tiny decisions you have to make when creating a component library (50 of them, according to Cory), but even if you make all the right ones, there’s a chance your team might decide not to use it for a number of reasons.

Some of the most common reasons you'll hear are that components are too bloated (they do too much), or too restrictive (they don’t do enough), or they don’t match a particular app’s style.

To prevent this, Cory shares the seven keys for successful component libraries. You should definitely watch the talk to learn more about them, but here’s a sneak peak of my favorite ones:

  • Start small: don’t try to make "the one component library to rule them all" right from the start. You can set a goal to build a library for a specific project, and from there you can grow it to serve an entire team, your entire organization, and finally, the general public if you decide to open-source it.
  • Start rigid: be opinionated about your design decisions. Don’t try to satisfy the needs of every developer using the component library, and only add new props to your components when necessary.
  • Use the Rule of Three: before adding a new component to the shared library and making it reusable, make sure it actually belongs to the library by using it in at least three different places.

ARCHITECTURE SNACKS

Links Worth Checking Out

One of last week's big announcements was the introduction of Runes, the upcoming Svelte 5 API, which brings runtime reactivity based on signals to Svelte.

Speaking of Svelte, Geoff wrote a comprehensive guide explaning how streaming works in SvelteKit and how we can do it conditionally using promises.

Great advice to keep in mind next time you have to deal with a forced rebuild or migration: "when complexity leaps are on the table, there’s usually also an opportunity to squeeze some extra juice out of the system you have."

An extensive and well-organized list of software and other tools with free developer tiers. Perfect for that side project you've been procrastinating on for the past six months.

Grug brain developer might not be a very bright individual, but they certainly have some good advice on how to build software.

BOOKSHELF

Grokking Simplicity

Before finding Grokking Simplicty by Eric Normand, all of my previous attempts at learning about functional programming (FP) failed miserably.

Things usually started well, but as soon as the academic jargon and the code examples in Haskell showed up, my brain would immediately shut down.

Thankfully, this book is different.

Unlike many other functional programming books, Grokking Simplicity puts the mathematical concepts of FP to the side to focus entirely on managing software complexity. In particular, it explains how we can build better software by adopting two of the main ideas in functional thinking:

  • Distinguishing actions, calculations, and data. Actions are procedures with side effects, like a sendEmail function or a deleteUser method. Calculations are pure functions (e.g., a sum(a, b) function) that will always behave the same no matter how many times you call them. Data is the inert component: things like objects, strings, or numbers. Identifying these and not mixing them up is one of the main traits of a functional programmer.
  • Using first-class abstractions. This is about composition and using higher-order functions (passing functions to other functions) to reuse code in an organized way. This section of the book also covers working with timelines (e.g., handling asynchronous operations) and a discussion about reactive vs. onion architecture.

There is a lot I love about this book. For starters, it’s a practical book for software engineers, which isn’t the norm for functional programming literature. The book is also really easy to digest, thanks to the many illustrations and annotated code examples that make the content easy to follow. And, as a bonus for frontend engineers, the code samples are entirely written in JavaScript—so nothing will get lost in translation.

If you're looking for a gentle introduction to FP, or want to learn new techniques for managing complexity in your applications, I highly recommend you check this one out.

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

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