A pattern we see in every legacy modernization
When we start a modernization engagement, one of the first things we do is walk through the git history. The code that became unmaintainable — the code that triggered the call to us — almost never got that way because the original developers were bad engineers. It got that way because each team added a layer of cleverness, a new framework, a build-time abstraction that "solved" a problem by hiding it.
The most durable codebases we've worked on have one thing in common: they made boring choices.
What boring means (and doesn't mean)
Boring technology isn't old technology. It's technology with a long track record, a stable API surface, and a large community of engineers who understand it and can debug it at 2 AM.
SQL is boring. HTTP is boring. REST is boring. These aren't weaknesses — they're advantages. Every developer you hire already knows them. Every monitoring tool already supports them. Every StackOverflow question has already been answered.
Boring also doesn't mean "don't learn new things." It means new things earn their place by solving a specific problem that existing tools can't — not by being interesting.
The cost of novelty
Novel technology carries hidden costs that rarely show up in the initial proof of concept:
- Hiring is harder. The smaller the community, the shallower the talent pool.
- Debugging is harder. Less community knowledge means more time in documentation and source code.
- Upgrades are harder. Smaller ecosystems have more breaking changes and fewer migration guides.
- Onboarding is harder. Every unusual choice is a new thing the next engineer has to learn.
These costs compound over time. A microservice built on a niche framework in 2019 is often the thing that blocks deployment pipelines in 2024.
How we apply this in practice
When we're architecting a new system or a modernization path, our default stance is: use the most established tool that solves the problem. We reach for something newer only when:
- The established option has a documented, meaningful gap for this use case.
- The team already has experience with the newer tool.
- The maintenance burden of the novel choice is explicitly budgeted.
That's it. The bar is intentionally high.
"But what about staying current?"
Staying current matters. Unmaintained technology is a different kind of risk. But "current" doesn't require "newest." .NET 8 is current. PostgreSQL 16 is current. React 18 is current. These are all boring in the best way.
The goal isn't to avoid modern technology. It's to avoid technology that hasn't proven itself under load, in production, at the kind of scale your organization actually runs at.
Boring wins in the long run. We've seen it too many times to think otherwise.
See our modernization approach for how we structure these decisions in a real engagement.