How to Write Shy Code

Decoupling, orthogonality, and the principle of least knowledge in JavaScript.

Issue 30 /

Hey friends đź‘‹ Welcome to September! Wait, that can't be right. What happened to August? Or to the entire first half of the year, for that matter? Anyway, if you're in the US, I hope you have a fantastic Labor Day weekend and that you celebrate by not doing any labor at all. And if you're not in the US, I hope you enjoy a very quiet Monday at the office.

Some of you might be wondering what happened with the Fundamentals course. Wasn't it supposed to launch last week? Why yes, yes it was. I don't want to bore you with excuses, so all I'll say is that the release is running a bit behind schedule. The course is pretty much done though, and I'm committed to launching it before you get the next issue of the newsletter, so stay tuned!

Today we'll talk about a handful of useful programming principles, we'll look into the future of JavaScript, and we'll learn what "orthogonality" means—which, surprisingly, has nothing to do with your ability to be a good orthodontist.

Let's dive in.

SOFTWARE DESIGN

How to Write Shy Code

Photo by Deeana Arts on Pexels.com

No, I'm not talking about code that blushes when you compile it.

And yes, I'm well aware of how terrible that joke was. But here's the thing: this is my third attempt at a funny intro about how code can be shy, so if you thought that joke was bad, you really don't want to read the other two.

There is a bit of truth in that first sentence, though—my code may not blush when I compile it, but my terminal does tend to turn red when I try to run it.

Ok, I'll stop now.

I'm talking about what Andy Hunt and Dave Thomas refer to as shy code, which is “modules that don’t reveal anything unnecessary to other modules and that don’t rely on other modules’ implementations.”

Why is this important? The short answer is that by writing shy code, we promote all of the good things we care about in software design: encapsulation, loosely coupled modules, and orthogonal systems.

The long answer… well, it starts with defining what that weird word I just used means.

Orthogo—what?

If there was a list of words that computer scientists and very senior engineers like to use the most, I’m pretty sure orthogonality would be at the very top.

It’s one of those words that, if you bring it up in a conversation with me, I’ll nod in agreement to indicate that I think I know what you’re talking about—but I’ll also try to change the subject before you ask me to define it.

Despite its complicated name, though, orthogonality is not really a complicated subject. In fact, it’s something you’re likely very much aware of.

The term comes from the world of geometry and mathematics, where it's used to indicate that two lines or vectors are perpendicular or independent of each other.

The x, y, and z coordinates are orthogonal—moving across one axis doesn't affect the other two.

In software design, we typically use this term to talk about independence between modules in a way that eliminates side effects between unrelated things.

When two modules or components of a system are orthogonal to each other, changes in one of them are unlikely to affect the other.

The canonical example in software development is the relationship between your user interface and your database. Changing the color of a button in your UI is unlikely to affect your database—and vice versa—so we can typically say that the UI is orthogonal to the data layer.

Unless, of course, you’re running SQL queries from your CSS, but that’s probably a story for another day.

At the code level, we can measure orthogonality by observing how changes propagate through the codebase. If a change in function A requires a change in function B, even though A doesn’t depend on B directly, then we have a clearly non-orthogonal system.

Writing code that exposes as little as possible about its internal structure to the rest of the world (i.e., shy code) is a great way to promote orthogonality.

That’s easier said than done, of course. But there is a trick to writing shy code effectively—all we have to do is follow the law.

The Law of Demeter

Legend has it that Demeter, the Greek goddess of harvest and agriculture, was an incredibly talented software engineer with zero tolerance for leaky abstractions.

One day, after a particularly frustrating code review, Demeter grew so tired of her coworkers' tightly coupled code that she swiftly stood up from her Aeron chair, took her noise-canceling headsets off, and proclaimed her divine law:

“Thou shalt speak only to thy closest companions, for reaching into the depths of a stranger’s soul shall bring chaos to thy code as surely as I bring the winter when my tests flake on the release pipe lines.”

Ok, none of that is true. But it would make for a cool story, huh?

The real Law of Demeter was established by Ian Holland in the 1980s and says that an object (or function) should not know anything about the internal structure of the objects it manipulates.

“Only talk to your friends” is a common way to summarize this law, meaning that an object should only call methods of its direct dependencies—not of its dependencies’ dependencies, or its dependencies’ dependencies’ dependencies, or… well, you get the idea.

In case that still doesn’t make sense (and I wouldn’t blame you, honestly), here's a quick example.

Imagine we have these three modules in a customer support system: Customers that contain Conversations and Conversations that contain Messages.

Some of the building blocks of our customer support system

Now, say we want to write a function that grabs a customer’s most recent message from their most recent conversation to process it somehow. The actual processing of the message is not important here, so I will leave it up to you to imagine whatever it is we’re going to do with it.

The code for this function might look something like this:

Our initial processing function

To be honest, this code isn't too bad, but it is a clear violation of the Law of Demeter.

With this implementation, our function needs to know not just about the Customer object (which is its one explicit dependency), but also about the internal structure of the Messages and Conversations objects—which are objects it doesn’t directly depend on.

At first glance, it might look like our function is only coupled to the Customer object, but if we look closer, we'll see that it's actually tightly coupled to all three modules.

Our function is a bit nosy—it needs to know a lot about the internal structure of various objects

The biggest problem here is that we’re losing all the encapsulation provided by the Messages and Conversations objects. We’re traversing multiple levels of abstraction, which increases the level of coupling between our function and the rest of the system.

Say we had to make a change to the findFirst/findLast functions to account for the customer’s timezone (it’s always timezones, huh?) This change would likely end up impacting our processing function, even though it has nothing to do with timezones or how messages and conversations are ordered.

A shy version of this logic, on the other hand, could look something like this:

The shy version of our processing function

Our function now only knows about a method that is part of the Customer interface, but nothing about the internal structure of Messages and Conversations.

Note that this isn’t about eliminating dependencies. The dependencies between these modules still exist (and should probably be represented in shy code as well), but they’re now better encapsulated, limiting the knowledge our function needs to have to a single layer of abstraction.

We've limited the knowledge of our function (the orange arrow) to a single layer of abstraction

If we had to apply our timezone change now, we’d probably still need to change the implementation of our findLast/findFirst methods, but we wouldn’t have to change our processing function because it’s independent of the interfaces of the Conversations and Messages objects.

Or, as a computer scientist would say, we’ve improved our design by making these modules orthogonal to each other.


The example we just saw also illustrates an object-oriented programming principle known as “Tell, Don’t Ask“—instead of asking an object for some data and then acting on that data, it is better to just tell the object what to do.

Tell, Don’t Ask is a great way to keep our code shy, and like many other OOP principles, it’s a universal one: we can apply it to functions, UI components, and React hooks alike (no classes or inheritance necessary.)

When taking it to the extreme, we could think of this principle as “don’t chain method calls,” but if you ask me, that’s taking things a bit too far: if the methods we’re changing all belong to the same object (think of methods that return this), then there is absolutely nothing wrong with chaining them.

At the end of the day, all of these laws, guidelines, and principles boil down to a simple question: How many layers of abstraction is your code traversing?

One is ideal. Two is okay. Three or more, well, that might be an indication that your code knows too much and is maybe a bit too bold. If you want to improve its design and make it more resilient to change, try making it a bit more shy.

ARCHITECTURE SNACKS

Links Worth Checking Out

Converging Web Frameworks by Minko Gechev

đź“• READ

  1. As further evidence that we can apply OOP principles even without writing OOP code, Alex Kondov wrote about how we can apply the Interface Segration Principle (the I in SOLID) in React.
  2. Matt Pocock wrote a step-by-step guide on how to publish an NPM package, which, as you might very well know, is not as easy as it sounds.
  3. If you're looking for an in-depth (but not too long) explanation of what JavaScript generators are and how they work, check out this great article by Jan Hesters.
  4. Frank Fiegel discovered that LLMs are pretty good at unminifying code, so I guess it's time to add "reverse-engineering expert" to the list of jobs AI will replace in the next year.
  5. Is that a regular expression or is your cat playing on your keyboard? It's hard to tell honestly, but this article by Steven Levithan on the newest features of JavaScript regexes might help us figure it out.
  6. Thorsten Ball wrote about one of my favorite productivity hacks for writing words and/or code: leave something for tomorrow.

​

🎥 WATCH

​Converging Web Frameworks by Minko Gechev

JavaScript frameworks love to argue with each other about their differences, but if there's one thing they all agree on, it's that signals are the way to go. In this talk at the latest dotJS conference, Minko Gechev from the Angular team talks about how signals work in the framework's latest version, what cool new features are coming in the near future, and what his team is learning by merging Angular with Wiz—Google's less popular internal framework.

​

🎧 LISTEN

​Evolving JavaScript with Douglas Crockford

I really enjoyed this conversation between legendary programmer Douglas Crockford and TypeScript expert Josh Goldberg. You might know Crockford as the author of the classic JavaScript: The Good Parts and for being the creator of JSON, but what you might not know is that he's one of the main reasons the JavaScript community is as strong as it is today. Crockford was one of the few people who stood up for JavaScript in its early days, at a time when it was considered nothing more than a toy language by more serious programmers. He proved that you could build serious stuff with JavaScript, and he documented his learnings along the way so that we could all learn from them. If you're curious to hear what he thinks of the past, present, and future of the language, this podcast is a great 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

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