đ Hello!
For a change, the weather in London was beautiful this week. I think thereâs roughly two groups of people in the UK:
Those who welcome the hot weather
Those where any sustained temperature above 15c is a mortal threat
Iâd put myself firmly in the first bracket. Long may it last. âď¸
If this is your first time here â hello! I write a weekly newsletter giving guidance and techniques building better software and being more effective in high growth companies. I mix in longer form content â think a short blog post that you could read in 5 minutes â with some of my favourite links that Iâve seen over the week.
Hereâs what Iâve covered over the past month:
I have a request of you! This is still a niche newsletter. The most helpful thing you can do is to spread it to others that might enjoy it. Post it internally at work, retweet it, or simple send it to a friend. Iâd really appreciate it.
All the best,
Stephen
Writing maintainable code at speed đ
Iâve always loved to work really quickly â sometimes to my detriment. I put this down to only working in âdefault deadâ startups, where speed really is one of the only advantages you have.
When shipping is your companyâs heartbeat, speed is generally good thing to optimise for. But how do we avoid ending up with a spaghetti mess of a codebase in the meantime?
This edition focuses on techniques you can use to keep your codebase healthy, despite rapid change.
What does maintainable mean?
For every line of code written, a maintenance cost is incurred â the cost of supporting that code into the future.
This cost may be explicit â such as a flaky third party provider that causes on-callers to be paged. Or, implicit â a confusingly written system with few tests. Productivity and happiness drops whenever you interact with this system.
Over time, as traffic scales, or the number of engineers grow, these fixed costs grow larger and larger. Itâs reasonable to assume that as a company scales up, they should speed up. For many reasons, this often isnât the case.
One of those reasons is the difficulty of maintaining and extending their current systems. If we want to go fast, we must minimise this ever-growing, fixed cost of maintenance. Velocity must grow super-linearly over time.

Techniques you can use
So, how do we go quick, but keep things maintainable? Hereâs a couple of things that have worked for me in the past.
A note before we start. I steer away from things that may be very hard to change, are specific to one language or tool, or are overly personally biased. For example, I totally believe a statically typed programming language really helps maintainability in rapidly changing codebases. But I think youâre unlikely to re-write your entire codebase in Go â at least, I hope not as a result of a paragraph in this newsletter. Instead, I favour techniques that are applicable to a wide audience.
Boxing up cruft đŚ
Speed is a constraint. Constraints may force you to write code that isnât particularly maintainable, or youâre not particularly proud of. You might cringe when you see it. Donât worry. Itâs called cruft. Cruft is the sort of code that makes you squint your eyes when you look at it, or think âwho on earth wrote this?â. Cast those dispersions aside. Used correctly, itâs a great tool.
So, how do we use cruft well? Thereâs a key concept to understand: the same piece of code can have a different cost of maintenance, depending on how other parts of the system interact with it.
One way to decrease the maintenance cost of cruft is to encapsulate it behind an interface. Letâs go through an example.
Despite recently reaching a market capitalisation of over $1TN, weâre convinced that the era of Amazon is over. Weâre going to start our own e-commerce store, and weâd like to launch it in a week. One area that we just donât have time for in the first instance is building a fancy recommendation system. Weâre going to have to do something crufty. What should we do?
In this case, Iâd do the simplest thing that you think might work OK, and hide your implementation behind an interface. Iâve never worked for an e-commerce store, but I imagine sorting by the popularity of an item works OK in a pinch. Itâs a bit crufty, but we can come back to it later.

Why does hiding behind an interface matter?
Semantically, youâve explicitly stated that the consumers of the system should care about the ends, not the means.
Practically, youâve hidden the mechanics of how your implementation works, so it doesnât bleed into other systems. This means you can return to your crufty implementation later on, and improve it.
Donât be ashamed of judiciously-used cruft: but hide it well!
De-couple systems and teams liberally đ¨
âCouplingâ is the extent to which sub-components of your system depend on each other. If to change a lot of system A, you must also change a lot of system B, A and B are highly coupled. If you can make large changes to system A, but system B needs minimal or no changes, A and B are lowly coupled.
This is a further development of our âboxing upâ principle, applied at the scale of systems and teams.
Letâs go through a hypothetical example, with a company called IceCream2U. They deliver authentic Italian gelato to you, wherever you are in the world, for just ÂŁ5. Unfortunately, some pesky fraudsters have grabbed a bunch of stolen credit cards and are causing massive losses to the company through chargebacks. A new team has been spun up to integrate with a fraud detection service (I know a good one if youâre looking!).
In this example, we have two systems, worked on by two teams:
A newly created fraud detection system, that is being written by the Fraud Detection team. Theyâre tasked with spotting fraudulent payments and preventing them, whilst providing a good experience to genuine customers. Theyâve been told that they should launch as soon as they are ready â every day that they donât, the business loses more money.
An existing billing system, owned by the Payments team. This service will call out to the fraud detection system to determine whether it should accept the payment. They already have the ability to reject payments.
Even though we have to build at speed, how can we avoid coupling the two teams and systems unnecessarily? Hereâs how Iâd approach the problem.
Fraud detection team
On the first day, define the types of decisions that youâll return as part of your interface. Accept and reject seem like sensible places to start: we can make changes later on. Then, create your new âfraud detection serviceâ. To start with, it will allow every transaction that it is passed.

Payments team
On the second day, integrate with the new fraud detection service by calling it. For safety purposes, ignore any decision that isnât accepting the payment, and alert the Fraud Detection team when this is the case.

With trivial amounts of work, weâve now got the foundations in place. The Fraud Detection team can get to work on building a great system to predict fraud with the data theyâre being passed, knowing that thereâs no possible way that their system can reject any live payments.
Letâs fast-forward a little. The Fraud Detection team now has a promising model for predicting fraud. Theyâre ready to launch an experiment where theyâll send a percentage of payments to the new system and start rejecting payments. They send a pull request to the Payments team, removing the âsafety codeâ that was added â they now want the ability to reject payments.

They roll out their change, but with the experiment switched off. The system is still accepting all payments.

Later on in the week, they launch their experiment. Theyâre now stopping fraud. đ
This example highlights a few important themes:
Going quick doesnât always mean trading off maintainability. Nothing in this example is crufty.
Synchronisation between teams can kill speed. If you must synchronise, itâs best to synchronise against interfaces, early on. This is a âformalâ way of specifying how your systems will interact with each other. Once youâve put the foundations in place, teams can work independently of each other against these interfaces.
Smaller changes are generally safer changes. At each stage, we didnât change much, but ensured that it was safe.
Static analysis
I believe static analysis is the single most underrated tool for maintaining quality at speed:
It is an automated enforcer of norms within organisations.
It relieves engineers from the mental burden of spotting and pointing things out at code review.
It tightens the loop between writing code, and seeing whether itâs acceptable to the organisation.
It can explicitly target maintainability, not just correctness, by analysing the way the code was written. This is challenging to do in any other automated way.
Iâm going to write a whole issue about it soon. đ
Fast and reliable unit tests
First, a carefully maintained test suite speeds you up over time, and provide guardrails against future regressions.
Second, as touched on above, theyâre a great way of reducing the time in a feedback loop as a developer from typing to understanding whether your code is acceptable or correct.
Best of the internet đ
Every week, I collate some of my favourite links to share. Found something cool, or built something great? Send it to me by replying to this email and I might include it in the next edition. đ§
Is High Quality Software Worth the Cost? - Martin Fowler
If youâre interested in what I discussed above, this is a must read. Martin argues that to consistently achieve speed over time, you need to maintain high internal quality: âhigh internal quality reduces the cost of future featuresâ.
Load bearing quirks - @mcclure111
I thought this was a really well put thought. In code, itâs difficult to communicate which quirks are intentional, and which arenât. This is why I get frustrated with the anti-comments-in-code crowd.
Causal - Taimur Abdaal, Lukas Koebis
Causal is a beautiful tool for playing with numbers. I found this on Saturday, and spent the afternoon with it. Itâs really nice to be able to explicitly input probability distributions within Causal, and have the tool perform simulations for you. I think itâs going to be big.
Thatâs all from this weekâs High Growth Engineering. If you enjoyed it, Iâd really appreciate it if you could do one of the following:
Share it with a friend that would find it useful.
Follow me on Twitter: @sjwhitworth
Subscribe: just hit the button below.
All the best,
Stephen