AM013 – Easy-to-Write Code Considered Harmful

Ever wondered why some old projects from former employees are so damn hard to read and maintain? I’d like to argue that the original authors carried around an implicit context that was not made explicit in their work.

Ever wondered why some old projects from former employees are so damn hard to read and maintain?

I’d like to argue that the original authors carried around an implicit context that was not made explicit in their work.

This implicit context is spread around in all the tiny decisions that aren’t entirely obvious in the code:

  • undocumented magic numbers
  • emerging patterns without guidelines
  • default values without a hint of the reasoning behind them
  • code that doesn’t immediately tell you where it comes from or goes to

We find it hard to work on these codebases because we have to pay the cost of reconstructing that context from these fragments every time we engage with it.

Since all code relies on some implicit context, the trick is to make the bulk of that context explicit.


Example: React Hooks and Destructuring Imports

Roy Derks recently posted on Twitter a question.

Twitter Embed

From the answers, most people said they’d import symbols with destructuring imports in Javascript. This is a rather small concern, but everything adds to making a particular system easier or harder to work with. Imports are no exception.

This can be less cognitive overhead while you are writing it, because most of what you need to remember is in your working memory already.

When you just wrote the { useState } import, calling useState(0) will be dead obvious.

But what happens when your working memory is cleared?

The cost of mapping imported symbols to specific modules is paid every time someone reads the code.

As the complexity of your codebase increases you’ll pay more and more time every time you try to read the code.

React Hooks

Let’s look at a piece of React code with hooks:

// file: module.js
// ...several hundred lines of JS...

// some React component
const [enabled, setEnabled, resetEnabled] = useState(false);

It is impossible to know from this whether someone is misusing React.useState or if we are using CustomHooks.useState. I’m forced to go to the top of the module every time I want to see where this function is actually coming from.

The alternative would’ve been to write out:

// file: module.js
// ...several hundred lines of JS...

// some React component
const [enabled, setEnabled, resetEnabled] = React.useState(false);
// or 
const [enabled, setEnabled, resetEnabled] = CustomHooks.useState(false);

Writing out the module name helps us see that we’re in fact using the function wrong.

And the next time anyone reads this code they’ll immediately know where names are coming from.

This is what you’d call “Easy to Read” code.

Code that was written paying a little more upfront, so reading it is cheaper.

The Cost of Rebuilding Implicit Context

Easy to Write code is code that will be Hard to Read without that implicit context. And that context will absolutely need some effort to be built up again!

How much harder to read it will be depends on how much energy is spent while writing it explicitly to make it easier for other people to rebuild that context.

Think of:

  • New employees – anyone being onboarded to your codebase and team
  • Current employees that didn’t write this code – your peers that have to review your code
  • You – a week from today, when it breaks in production

And consider the cost of things like:

  • A default value without a good domain-specific comment explaining it means that someone will have to talk to your Product Owner to understand it. Let’s hope the new Product Owner also understood it the same way.
  • A new idiomatic pattern will likely need a link to the documentation that explains it. Depending on your team, some people will ask you to rewrite this in an already established pattern instead, or they’ll have to get familiar with it to work on your code.
  • Specific technical knowledge (an IETF RFC, library internals, etc) will likely take time to get familiar with. This may be justified, but pointing to the right resources will help.

So be kinder to your future self and whoever is going to maintain this.

Pay the cost upfront. Write code that is Easy-to-Read.

Ok, so how do I write Easy-to-Read code?

It’s easier to read than to learn how to write it, but if you’re here you’re either already trying or willing to, so lets get down to it.

First, identify why some code is easy to write for you now.

Then turn those reasons into explicit context in the actual code.

Do this a few times and you will write code that is a lot more aware of the things it takes for granted.

Your future self and your peers will thank you for it.

One last thing: There is a lot more I’d like to say about the Cognitive Overhead of Reading Code, and how we can pay it upfront to make it easier for others to onboard new devs into a codebase, and really for everyone involved to maintain that code over time. But I’ll do that more systematically on another occasion.

Subscribe to Abstract Machines

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe