Gresham's law of programming

Or, bad code drives out good

A man pinches the bridge of his nose, while surrounded by computer screens showing financial charts

You may have heard of Gresham's law before, even if you don't know it by name. It's the observation that "bad money drives out good." It's a phenomenon that's been observed in economics for about as long as there's been a study of economics. To quickly summarize, it applies when you have multiple circulating currencies with the same face value, but different intrinsic values. For instance, if you have coins of different materials; or bank notes backed by different standards. In that case, everyone in the market has an incentive to hold the more intrinsically valuable currency, and only exchange the less valuable one. In fairly short order, you'll find that only the less valuable currency is circulating. This effectively reduces the total value of the economy's monetary supply.

Now that we're on the same page, I can get to the main point: the exact same dynamic plays out with software development.

Source Code

To reformulate Gresham's law for source code: bad code drives out good. In the financial context, bad and good is about relative intrinsic values. Software doesn't have intrinsic value. At least not in the way that currency and commodities do. With software, bad and good is about relative measures of quality.

For software, good—or high quality—code is clear and comprehensible. It's narrowly scoped to a specific purpose. It's isolated, testable, and easy to evolve. It effectively models the domain. And bad code is not those things, or at least less so.

Combine this with the reality that software is rarely ever finished, and the result is that over time, code that's easy to understand and modify will continue to be modified until it is no longer easy. The bad code will have driven out the good. Work will move to new code that's easier (and safer) to work on, and the now difficult code has become "legacy". It's treated as a black box, and becomes a drag on the team's ability to iterate. If it gets bad enough, it can even become a barrier to what's possible for the team to implement.

This happens because there's incentive to write worse code. For one thing, writing bad code is easier. It's not as mentally demanding. It doesn't require the same level of familiarity with the system. It may even be faster, in the short term. At least it feels that way, and it's a common assertion. Although I'm not aware of any systematic research to back up that claim. But even if everyone involved displays superhuman discipline in their programming, changes can still degrade quality on accident. Yet code will likely never gain quality by accident. Just like metal coins won't spontaneously become more pure. There's just no mechanism for it.

In this light, you can view practices like linting, unit tests, design documents, and code review as being akin to monetary regulation. They form counter-incentives to introducing bad code, and inhibit its spread.

All Code, Actually

Speaking of unit tests, if your experience is anything like mine, that's where all of this is most apparent. If not in the unit tests themselves, then likely in the build system, the packaging, deployments, CI/CD, and generally all of the automation that surrounds your source code. All of that is also code, and all of the forces that lead to good code being driven out by bad still apply. Except in that case, there's likely little or no counter balancing force. After all, who tests the unit tests?

So, if it's hard to configure your development environment, you're seeing the result of Gresham's law. If deployments are slow or risky? Gresham's law. If tests are flaky? I'm sure you get the idea. All of those things were fast, easy, and reliable in the beginning. But they degrade over time. Good code that was modified until it was bad. And those things in particular tend to degrade rapidly, because they're not protected from it in the same way generally come to recognize that source code should be protected. Those things also tend not to be designed with the same care in the first place, which is a separate—but overlapping—concern.

Tech Debt

Yes, sorry, this is blog post about tech debt. Sort of. Bear with me. Despite how much it's maligned, I actually find tech debt to be a very rich metaphor. It's also a very persistent one; possibly because of this richness. It should be more useful than it is, but it unfortunately doesn't provide the kind of shared vocabulary we would hope. People with a software background tend not to have a lot of familiarity with business or finance; at least not in the routine operation of these fields, where debt is an expected element. This is in the same way that people in business and finance often aren't familiar with the routine details of programming software. So we end up talking past each other.

To make better use of the metaphor, it's necessary to understand that not all debt is the same. That's what makes it such a rich metaphor for the accumulation of quirks, bad patterns, and design mismatches that happens with software. Some debt is normal, or even good. It's a financing mechanism. But then some debt is mortgage derivatives, or student loans. The impact and risk of each is different. And so are the mitigations. In essence, you have to know what kind of debt you're dealing with, and how much.

So, Gresham's tech debt is the kind that accumulates progressively over time. The details can vary widely. Luckily for the purpose of this post, a lot of it is the kind of things that have named code smells or anti-patterns. Think of tight coupling, code duplication, or inner platforms. The sources are varied, but the impact isn't: it slows things down. When it gets bad, things feel difficult, risky, unmanageable, or stuck. A little bit of this isn't a big deal. But a lot is. If bad money is allowed to propagate unchecked, it can spiral into hyper inflation and other crippling economic problems. When Gresham's tech debt spirals, it leads to development paralysis and operational brittleness.

Managing Debt

The solution to this problem depends a lot on how bad it is. A little bit of bad code can be remediated. You make a plan and commit to continuous refactoring. In the debt metaphor, this is honestly fine. It's not so different from paying interest on a loan. You just do it. I think most of the time it doesn't even need to be approved, it's just expected. It's like cleaning a workshop or sharpening tools in a more tangible craft.

If it is serious, that's harder. The monetary response to a bad money crisis needs to be radical. For example, in the 18th century, England dealt with a bad money crisis by effectively moving from a silver standard to gold. In 2016 India responded to widespread counterfeiting by demonetizing and re-issuing about 15 trillion rupees. I give these as examples to demonstrate the the magnitude of the response. It's expensive, disruptive, and not to be done lightly. I hesitate to even suggest it. But the solution to a crisis of Gresham's tech debt may be a big bang refactor. To be honest, if you're calmly sitting here, reading my blog over coffee, you likely don't have a severe enough crisis to warrant that.

Wrap Up

My intent with this post is to provide some structure to the way we think about, and talk about tech debt. For the engineers reading this, know that you have to explain the kind of debt you're talking about. If you're looking for resources to mitigate a problem, you have to explain the problem and match the magnitude of your response to the actual severity of the problem. Because if you simply say there's tech debt and you want to refactor, it can sound to stakeholders like you're proposing some pretty extreme measures for a situation they don't even think of as a problem. Like declaring bankruptcy over the interest rate on your auto loan.

I discussed the big bang refactor here because while this is quite rare, it is sometimes necessary. It's sort of to define the scale. It puts other actions into perspective. It's much more like that your problem is actually high friction on code changes, and the response is a few design meetings plus support for the design proposals. Or something along those lines. The important thing is to actually say that. And to have a theory of action and change that you can explain to stakeholders. Gresham's tech debt is one such theory, that may apply to your situation.


Cover photo by Tima Miroshnichenko