We Need to Talk About Coupling

Is all coupling 'bad', or is there a 'good' kind of coupling? Also, why is it calling me on a Sunday morning?

Issue 27 /

Hey friends 👋 This past week (on July 16th to be exact) was Frontend at Scale's first birthday! I can't believe it's been an entire year since I sent the first issue of this newsletter, and I just couldn't be more grateful for the amazing support I received from you all during this time. Whether you've been here since day one, or this is the first time you get an email from me and are still wondering, "who the heck is this guy?"—I just wanted to send a big and fat thank you.

Also, thanks to everyone who signed up for early access to the Fundamentals of Frontend Architecture course over the past couple of weeks! It makes me so happy to hear that you're as excited as I am about this course, and I truly can't wait to share it with you all :) I'll be reaching out to you about it soon.

In today's issue, we'll talk about conquering complexity with refactoring, one of Astro's cool new features, Redux in 2024, and a pretty strange call I got this morning.

SOFTWARE DESIGN

We Need to Talk About Coupling

Photo by Tingey Injury Law Firm on Unsplash​

I just got off the phone with Coupling. It was… an interesting conversation:

— Maxi: Hello?

— Coupling: Hey mate, this is Coupling. Remember me? We met at Linda's Christmas Party a few years ago. Got a sec to chat? I need to get something off my chest. I’m getting tired of people talking about me like I’m some kind of disease or something. Everyone’s “gotta decouple this, gotta decouple that,” but no one is like, “oh, that coupling is kinda nice, isn’t it?” It's so frustrating. It’s like no one appreciates the hard work that I do, you know?

— M: Wait, who's Linda? Also, how did you get this number?

— C: Don't worry about those details. Listen, I heard you have a little newsletter about software design or whatever. You have to write about me and tell your audience the truth. You need to tell them my side of the story.

— M: It sounds like you want to decouple yourself from your bad reputation, am I right?

— C: …

— M: …

— C: …

— M: Hello?

— C: Yeah I’m still here. Look, can I count on you or not?

— M: Alright, let me see what I can do.

It’s hard not to feel bad for our friend Coupling, honestly. We often talk about it as if it were an inherently bad thing—a sign of poor planning and questionable design decisions, a problem to be fixed.

But the truth is that we couldn’t build anything of value without some level of coupling. Coupling is the glue that keeps our software together; and much like companies adding AI features to their products in 2024, or like Thanos in the Marvel Universe, coupling is largely inevitable.

This doesn’t mean that coupling is necessarily good, of course. Whenever we find it difficult to make a change to our codebase, or when we can’t fully understand how a particular feature works, it’s often due to coupling getting out of hand.

So maybe there are “good” and “bad” kinds of coupling? How can we tell them apart? To answer these questions, we need to dive a little deeper. And I can’t think of a better way to do that than by going straight back to the source.

My copy of Structured Design by Edward Yourdon and Larry Constantine

Back to the Source

Larry Constantine started talking about coupling and cohesion in computer science as early as the 1960s, as part of his work on the discipline of structured design—an approach that applied software design principles to the new and shiny (at the time) paradigm of structured programming.

In 1978, Professor Constantine published the Structured Design book along with Edward Yourdon, in which they formalized a definition for these concepts. Here’s how they described coupling:

The measure that we are seeking is known as coupling; it is a measure of the strength of interconnection. Thus, “highly coupled” modules are joined by strong interconnections; “loosely coupled” modules are joined by weak interconnections; “uncoupled” or “decoupled” modules have no interconnections and are, thus, independent […] Obviously, what we are striving for is loosely coupled systems — that is, systems in which one can study (or debug, or maintain) any one module without having to know very much about any other modules in the system.
​
Coupling as an abstract concept — the degree of interdependence between modules — may be operationalized as the probability that in coding, debugging, or modifying one module, a programmer will have to take into account something about another module. If two modules are highly coupled, then there is a high probability that a programmer trying to modify one of them will have to make a change to the other. Clearly, total systems cost will be strongly influenced by the degree of coupling between modules.

The first nugget of wisdom from this definition is that coupling is a measure of the strength of interconnection. This means we shouldn’t think of coupling as an all or nothing characteristic, but as a spectrum between loosely and tightly coupled modules—with a lot of room in the middle.

Coupling doesn’t only happen between modules, but also within modules (with whatever definition of “module” we’d like to use here; functions, classes, components, or actual JavaScript modules.) This intra-modular type of coupling is the “good” kind—the one that we are happy to support as long as the contents of the module are related to one another.

But “good coupling” doesn’t sound very appealing, so we call it cohesion—the degree of relatedness between a module’s elements.

Good vs bad coupling. Graphic credit: Wikipedia​

Another insight from this definition in Structured Design is that we can think of coupling as the probability that when working with a module, we’d have to change the behavior of a different one. The higher this probability is, the tighter the coupling is going to be.

This is why certain patterns and practices have an inherent level of coupling that is sometimes impossible to get rid of. Pass-thru functions, for instance, are by definition tightly coupled because there is a high probability that they’ll need to change if whatever the thing they’re passing through also changes.

Now, you might be wondering why all of this matters. Is there a point we’re trying to make, or was this whole newsletter about coupling just an excuse to make the silliest intro to an article ever?

Well, that’s part of it, I’m not going to lie. But the reason talking about coupling is so important is in the last sentence of its definition in Structure Design: coupling is directly related to the total cost of the system.

They are so related, in fact, that we could even argue that coupling is the cost of the system.

Constantine’s Equivalence

In his excellent book Tidy First?, Kent Beck introduces us to what he calls Constantine’s Equivalence, which, as you can probably guess, derives from Larry Constantine’s work in Structured Design.

The equivalence goes something like this:

  1. The cost of a piece of software is the cost of making changes to it—software that never changes, effectively has a cost of zero (at least when it comes to developer time.)
  2. The cost of making changes is approximately equal to the cost of the big changes—the type of changes that require us to go on a journey through the depths of our codebase (where the dragons hide), touching every file we encounter along the way.
  3. The cost of the big changes is approximately equal to the degree of coupling in the system, so we can conclude that
  4. The cost of software is approximately equal to the coupling of the system.
Kent Beck's Constantine's Equivalence

No wonder our friend Coupling has such a bad reputation. Coupling is the reason changing the color of a button takes an entire sprint or that telling a user it’s their birthday is just simply impossible. Coupling is cost, and we don’t like cost at all.

The way we address this problem is with software design. According to Kent Beck, the entire reason we design software in the first place is so that we can change it at a reasonable cost. So even if there’s value in coupling, there’s a lot of value (or cost reduction, I should say) in decoupling as well.

With this economic view of coupling, we can see decoupling as an investment rather than a maintenance cost. But how do we do it effectively? Can we decouple only the “bad” parts so that we’re only left with the cohesive ones?

In Structured Design, Constantine and Yourdon talk about the main factors that influence coupling in software systems. And we can use these factors to find practical ways to loosen things up:

  • Simplify the interface — simple interfaces (i.e. functions with fewer parameters, components with fewer props, and so on) reduce coupling because they minimize the amount of information two modules need to know about each other.
  • Prefer input-output coupling — input-output coupling (also known as data coupling) is when we only pass data back and forward between two modules (e.g. calling a sum(a, b) function.) Control or coordination coupling is when we alter the behavior of a module by passing, for example, a boolean flag that changes the inner workings of a function. Coordination coupling results in much tighter coupling than the input-output kind.
  • Prefer explicit vs implicit dependencies — when a module has an implicit dependency (e.g., its behavior depends on some global variable or on the type of environment it's running on), it increases the degree of coupling of all the modules that use it. Implicit dependencies lead to unknown unknowns, which just results in overall system complexity. We should try our best to make our dependencies as explicit as possible.
  • Prefer cohesive coupling — as we talked about earlier, this is the “good” type of coupling, which involves keeping related modules (or related elements within a module) as close as possible. It is expected for cohesive modules to be tightly coupled to each other, but they should be loosely coupled to unrelated ones.
  • Reduce the probability of changes propagating throughout the codebase — in A Philosophy of Software Design, John Ousterhout calls this phenomenon “change amplification” and names it one of the main symptoms of software complexity. As we just saw, this probability is actually just the degree of coupling of a system, and we can try to alleviate it by keeping together the things that change together.

You’ll often find than decoupling things in some way results in increased coupling in some other way, which can feel… frustrating to say the least.

But that’s just the nature of how we build software—we can’t optimize for every possible change, so we need to prioritize the parts of the codebase that change more often (and, if possible, the types of changes that are more likely to happen.)

Remember that coupling is about probabilities, and those will change over time as your product and codebase continue to evolve.

So, is our buddy Coupling completely off the hook? Absolutely not. But I think we could be a bit more appreciative of its value. Just like we can’t make an omelet without breaking some eggs, we can’t really build a modular system without some level of coupling.

I hope that today’s article helps you see coupling in a different light, that you've learned a few ways to effectively decouple your codebase, and, more than anything, that these principles of software design don't make a habit of calling me every Sunday morning.

ARCHITECTURE SNACKS

Links Worth Checking Out

Conquering Complexity by Phil Nash

đź“• READ

  1. The Astro team wrote about one of the framework’s latest (still experimental) features: Server Islands, an extremely cool new primitive that lets us serve static HTML shells and inject them with dynamic content.
  2. The results of the very first State of React survey were just published. Go check it out to learn what everyone’s favorite React hook is.
  3. Marcus Buffett wrote about the advice he’d given himself 15 years ago. It’s all fantastic advice, but “Buy Bitcoin” would have been at the top of my list, honestly.
  4. Dr. Axel Rauschmayer updated his beginner-friendly Exploring JavaScript book to the ES2024 version of the language spec, and you can read all of it online for free.
  5. This four-part series on writing good design docs by Joel Spolsky, which he wrote 24 (!!) years ago by the way, is still one of my favorite resources on technical writing ever.

​

🎥 WATCH

​Conquering Complexity by Phil Nash

I’m a simple man—I see a talk about software complexity, and I watch it (and I’ll probably like it, too.) In this talk at the latest JSNation conference, Phil Nash tells us what software complexity is exactly, talks about a couple of different ways to measure complexity in our codebases (cyclomatic and cognitive complexity), and shares a few ways in which we can reduce that complexity via refactoring. If you often find yourself overwhelmed by your own code (and who doesn’t these days?), I highly recommend giving this one a watch.

​

🎧 LISTEN

​Why you should use Redux in 2024 with Mark Erikson

Well, that’s a good question, isn’t it? Redux is far from being the shiniest tool in the modern frontend development stack, but according to Mark Erikson, who has been a Redux maintainer for several years now, it remains a powerful option for state management in 2024. In this interview on the PodRocket podcast, Mark talks about the history of Redux and other React state management tools, how the project became a victim of its own success when developers started using it for pretty much everything (ahem, not me of course), and how Redux Toolkit makes the experience of using this library much more enjoyable.

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