Every few months someone on a team proposes a new tool, and the conversation takes one of two familiar shapes. Either the enthusiasts win and the stack grows another moving part, or the sceptics win with “we don’t need that” and the conversation ends without anyone saying what the real objection was.
The real objection is rarely the tool itself. It’s that every technology you adopt comes with a set of failure modes, and the question is whether you already know what they are. That’s what “boring” means, and it’s a much more useful idea than “old”.
Boring means the failure modes are known
The argument has a canonical source. Dan McKinley’s 2015 essay Choose Boring Technology made the case better than most of what’s followed it, and its central device is worth stealing outright: innovation tokens. A team gets a small, fixed number — he suggested about three — and every genuinely new thing it adopts spends one. A new database, a new language, a new deployment model, a new messaging framework: each is a token. When they’re gone, you’re done innovating until something new becomes old.
The point isn’t that new technology is bad. It’s that the cost of a technology isn’t paid at adoption. Adoption is the cheap bit — a spike, a proof of concept, a demo that works. The cost arrives later and in a different currency: in operations, in the incident where nobody on call has seen this failure before, in the upgrade nobody planned for, in the interaction with something else in the stack that neither tool’s documentation mentions.
A boring technology is one where that bill has already been paid, mostly by other people. Its failure modes are written down. Someone has hit your error message before and the answer is a search away. The person you hire next year has used it. The engineer paged at three in the morning can reason about it without first learning how it thinks.
That’s a non-functional requirement hiding in plain sight: operability by the team you actually have, not the team in the conference talk.
Where the token actually gets spent
A worked example, because the abstract version is easy to agree with and hard to apply.
On one platform I worked on, we chose a messaging framework that was well regarded but far from the default in its ecosystem. It was a defensible choice. It gave us a transactional outbox out of the box — the answer to the dual-write problem — and that’s exactly the kind of hard problem you want a library to solve for you. It was a token well spent.
But it was a token, and here’s what spending it looked like in practice:
- A bug in one of its storage integrations that filled the logs with errors on every node. The fix was a single line upstream — and it shipped only in the next major version. There was no patch release on the line we were on, so a one-line fix became a major-version upgrade.
- An interaction nobody documented. Adding the database library’s standard transient-fault retry, the textbook move for a cloud database, broke the framework’s outbox outright: the retry strategy refuses the explicit transaction the outbox depends on. Turning it on took nearly a third of one service’s API tests from green to red. The fix was to do retries the framework’s way instead — knowable, but only by someone who already understood both libraries.
- A deployment-time race that looked like lost messages and turned out to be ordering, which took real diagnosis to tell apart.
None of that makes it a bad choice. It makes it an expensive one, in exactly the currency McKinley warned about: specialist knowledge, concentrated in a few heads, paid for in debugging hours. That’s the trade. You accept it when the thing you get is worth it — and the outbox was. What you shouldn’t do is spend tokens like that on things that aren’t your problem.
Spend novelty where it’s your edge
So the useful question isn’t “is this technology good?” It’s “is this where we want to be different?”
Most of a system isn’t where a business differentiates. Your logging, your deployment pipeline, your web framework, your relational database, your identity provider: customers will never notice whether these are interesting, only whether they work. Those are the places to be thoroughly, deliberately boring, and to save the tokens for the small number of places where being unusual actually buys you something.
Two filters help:
- Reversibility. A new library behind a narrow interface is a two-way door: try it, back out if it disappoints. A new primary datastore is a one-way door: your data, your operational knowledge and your hiring plan all move through it. The less reversible the choice, the higher the bar for novelty — and the more it’s worth designing for deletion so that today’s exciting choice can be removed when it stops being one.
- Whose problem it solves. “It would be more pleasant to work with” is a real benefit, but it accrues to the people building the system. “It removes a class of failure our customers feel” accrues to the business. Both count; only one usually justifies a token.
And the most common way tokens get wasted isn’t one big exotic choice. It’s ten small ones, each reasonable alone: a second queueing technology because one team preferred it, a second frontend framework for one app, a niche database for one feature. Each is a separate set of failure modes, upgrades and expertise. That’s the distributed systems tax in another form: variety is a cost you pay on every incident, not just the first.
Boring has to be maintained
Here’s the part the original argument underplays, and the part that has cost me more than any exotic choice: boring is a state, not a decision. You don’t choose it once. You keep it by paying a small, steady price, and the moment you stop paying it, boring quietly turns into risky.
Three ways I’ve watched it happen:
- The licence changes under you. A PDF library we’d used for years moved to a licence our use couldn’t accommodate, and removing it was a real migration, not a version bump. The removal had to be total, too — the licence obligation attaches to what you ship, not to which code path runs, so the usual safety net of a flag with both implementations side by side wasn’t available. Several of the most widely used libraries in the .NET ecosystem moved to commercial licences in the space of a year. “Mature and everywhere” turned out not to mean “free forever”.
- The hosted dependency goes away. A documentation tool’s hosted service reached end of life, taking with it the themes our diagrams loaded at render time. A small thing, but a dependency nobody thought of as a dependency, with a hard date on it.
- The pin outlives its reason. We held a frontend framework at an older minor version to avoid a behaviour change in the newer one. Sensible at the time. Then the security advisories started landing with fixes only on the versions beyond the pin, and the pin that was protecting us became the thing stopping us from patching. Staying still had become the risky choice.
The general rule: an unmaintained boring stack is not boring, it’s a slow-motion migration you haven’t scheduled. Its failure modes are known today because it’s current. Fall far enough behind and you’re back in novel territory — running a combination nobody else runs any more, with documentation and answers that describe a world that has moved on.
So keeping technology boring has a budget line, and it’s a modest one: routine upgrades while they’re small, a dependency inventory someone actually reads, licence and end-of-life dates treated as the deadlines they are. It’s cheap compared with the alternative, which is doing all of it at once, in a hurry, because an advisory or a renewal forced your hand.
The honest limits
- Boring can be an excuse. “We’ve always used it” is not the same argument as “its failure modes are known”, and a team that never spends a token eventually finds that the boring choice has become the obsolete one. Fear-driven architecture is as real as CV-driven architecture; it just makes less noise.
- Boring is relative to the team. A technology that’s mundane to one team is exotic to another. The test isn’t what the industry thinks of a tool; it’s whether your on-call rota can operate it. That changes when people join and leave, which means a token can be refunded — or quietly spent again — by a hiring decision.
- Some problems genuinely need the new thing. If the boring options can’t meet a real requirement, spending a token is the right answer. The discipline is in proving the requirement is real, not in refusing to spend.
The one-line version
Choose technology for its failure modes, not its features: be boring everywhere customers can’t tell the difference, spend novelty only where it’s your edge, and remember that boring is something you maintain, not something you chose once. It depends, as always — but on what you can afford to learn the hard way.