Code Entropy: The Silent Killer of Engineering Velocity
Or: Why Your Codebase Is Slowly Dying and Nobody’s Talking About It
There’s a concept in physics that every software engineer should tattoo somewhere visible: entropy. The second law of thermodynamics tells us that in any closed system, disorder always increases. Things fall apart. Gardens become overgrown. That pristine codebase you launched eighteen months ago? It’s currently decomposing into something that makes your new hires question their career choices.
As the physicist Arthur Eddington put it, “The law that entropy always increases holds, I think, the supreme position among the laws of Nature.” He was talking about the universe, but he might as well have been describing that microservices architecture you inherited from the team that “moved fast and broke things” — emphasis on the breaking.
I want to introduce you to a term I’ve been using with my teams here at Agoda: code entropy. It’s the gradual, inevitable decay of code quality over time. Not through malice, rarely through incompetence, but through the simple accumulation of decisions made under pressure, shortcuts taken with good intentions, and the slow drift from “we should fix that” to “don’t touch that, nobody knows how it works.”
What Code Entropy Actually Looks Like
You’ve seen it. You might be living in it right now. Code entropy manifests in four distinct flavours, each more insidious than the last.
Structural entropy is the most visible. It’s the god classes that have accumulated responsibilities like a hoarder collects newspapers. It’s the tangled dependencies where Service A calls Service B which calls Service C which, inexplicably, calls Service A again. It’s the cyclic imports that make your IDE draw dependency graphs resembling abstract art. And it’s those “just this once” exceptions that have somehow become permanent fixtures — architectural decisions made at 4 PM on a Friday that are still haunting you three years later.
Semantic entropy is subtler but equally destructive. We’re talking about variable names that actively lie to you — a userCount that actually stores a timestamp, or a processOrder() method that, through years of feature additions, now also sends emails, updates inventory, triggers webhooks, and occasionally orders pizza. Business rules that should live in one place have metastasised across layers like an organisational cancer, making changes feel like archaeology.
Behavioral entropy is where things get genuinely painful. This is when small changes cause unexpected breakages in seemingly unrelated parts of the system. It’s fear-driven development — that module everyone avoids because the last three people who touched it spent a week in debugging purgatory. It’s the heavy reliance on tribal knowledge: “Oh, you need to talk to Sarah about that service. She’s the only one who knows why it does that thing on Tuesdays.”
Process-driven entropy rounds out the quartet. Copy-paste coding instead of abstraction because “it’s faster.” Feature flags that were meant to be temporary in 2019 and are now load-bearing columns in your architecture. Deprecated code paths still running in production “just in case” — because removing them requires understanding them, and understanding them requires time nobody has.
Why Entropy Accelerates
Here’s the truly terrifying part: entropy isn’t linear. It’s exponential.
Early shortcuts feel cheap. You skip writing that abstraction because the deadline is tomorrow and you can “clean it up later.” The cost seems negligible — a few minutes saved, a feature shipped. But later changes must work around those shortcuts. Each workaround becomes a new constraint. Each constraint limits future options. Each limitation spawns more workarounds.
The renowned architect Christopher Alexander observed, “When you build a thing you cannot merely build that thing in isolation, but must repair the world around it, and within it, so that the larger world at that one place becomes more coherent.” We’ve been doing the opposite — building things that make the world around them less coherent, one commit at a time.
Eventually, you reach a tipping point where the cost of change exceeds the value of change. That’s when you start hearing phrases that should terrify any engineering leader:
“We can’t move fast anymore.”
“Everything takes longer than it should.”
“We need a rewrite.”
That last one is particularly dangerous because it’s usually wrong. Rewrites rarely solve entropy — they just reset the clock while preserving the organisational dynamics that caused the decay in the first place.
Can You Actually Measure This?
Not directly. Entropy isn’t a number you can pull from your CI pipeline. But you can observe its symptoms with uncomfortable clarity.
Watch your lead time for changes. When simple features that used to take days now take weeks, entropy is winning. Track your defect rate — not just total bugs, but bugs introduced per feature. Monitor how much code gets touched per feature; if adding a button requires changes across twelve files, you’ve got a coherence problem. Pay attention to your test signal-to-noise ratio: when your test suite produces so many false positives that engineers start ignoring failures, you’ve lost a critical feedback loop.
But the most reliable signal isn’t in your metrics dashboard. It’s in your engineers’ behaviour. When people start avoiding certain areas of the codebase, when “that’s not my service” becomes a common refrain, when new team members take months to become productive — that’s entropy made manifest.
Static analysis tools can hint at trouble. Cyclomatic complexity, coupling metrics, code churn patterns. But metrics are proxies. The real measure is friction. If your engineers feel like they’re wading through treacle to make simple changes, trust that feeling.
Fighting Back: What Actually Works
Let’s be practical. You’re not going to stop entropy entirely — that would violate the laws of physics. But you can slow it down, reverse pockets of decay, and build systems that are more resilient to disorder.
Continuous Small Refactors
The Boy Scout Rule — leave the code better than you found it — is your best weapon against entropy. But here’s where most teams get it wrong: they treat refactoring as a separate activity, something to schedule in a future sprint that never comes.
Refactor while changing code, not as a separate phase. If you’re in a file adding a feature and you see a method that could be clearer, make it clearer. If you notice a naming inconsistency, fix it. These micro-improvements compound over time. They’re also nearly impossible to block in code review — nobody argues against a renamed variable that makes the code more readable.
The challenge, of course, is that Scrum actively works against this. When you put a Product Owner in charge of the backlog, how do refactoring tasks ever get prioritised over features? The honest answer is: they usually don’t. Which is exactly why refactoring needs to be embedded in feature work, not separated from it.
Deletion as a First-Class Activity
Here’s a counterintuitive truth: deleted code reduces entropy more than rewritten code. Every line of code is a liability — it needs to be understood, maintained, tested, and secured. Dead code paths, unused features, deprecated modules — they’re not just clutter. They’re active contributors to cognitive load and potential bug surfaces.
Make deletion a celebrated activity. Track lines removed alongside lines added. When someone removes a thousand lines of dead code, that’s not just housekeeping — it’s making the system more comprehensible, more maintainable, more alive. I made t-shirts for it, and i give them out and post in a public slack channel about people that delete large amounts of code.

Strategic Code Ownership
Code ownership exists on a spectrum, and where you sit on it matters enormously for entropy management. Martin Fowler describes three models worth understanding.
Strong code ownership assigns modules to specific owners, and only those owners can make changes. Need something fixed in another team’s service? You have to ask them to do it. This creates bottlenecks and delays that often lead to the worst kind of entropy — developers copying code into their own modules rather than waiting for changes, creating duplication and drift.
Weak code ownership also assigns modules to owners, but allows anyone to make changes. The owners keep an eye on their areas and review what others contribute. This is where I’ve seen teams fight entropy most effectively. The owners become on-the-ground filters at code review time, catching entropy-inducing changes before they merge. They’re the ones who remember why that weird workaround exists and can guide others toward better solutions.
Collective code ownership abandons individual ownership entirely — the team owns everything, and anyone can change anything. This works beautifully when you have strong shared taste and mature engineering practices. It fails spectacularly when you don’t, because nobody feels personally invested in any area’s long-term health.
In my experience, weak code ownership is often the right starting point. It provides the benefits of dedicated stewardship while avoiding the bottlenecks of strong ownership. But it should be a stepping stone, not a destination. The goal is to reach a point where the team has developed enough shared taste and collective responsibility that you can move toward true collective ownership — where quality emerges from culture rather than gatekeeping.
Protect Healing Time
Deadlines are entropy accelerators. When you push a team to meet a deadline, they will make compromises. That’s not a character flaw — it’s rational behaviour under constraint. But those compromises accumulate, and if you never provide time to address them, you’re just borrowing velocity from your future self at ruinous interest rates.
The economist John Maynard Keynes noted that “the difficulty lies not so much in developing new ideas as in escaping from old ones.” Your codebase needs time to escape from old decisions that made sense once but don’t anymore.
I’ve found a two-pronged approach works best:
First, time-box short periods for “anything you want” time, a day a sprint, or a couple days or a week per quarter, something like this. Engineers will gravitate toward the small annoyances that make their daily work harder — the flaky test, the confusing error message, the deployment script that requires three manual steps. These aren’t glamorous improvements, but they compound into a significantly better developer experience.
Second, larger refactoring efforts need ROI justification. This is harder because the benefits of reduced entropy are diffuse and long-term. Use DORA metrics — deployment frequency, lead time for changes, mean time to recovery, change failure rate — to establish baselines and measure improvement. When you can show that a refactoring initiative reduced deployment time by 40%, you’re speaking a language leadership understands.
The challenge here is trust. Not everyone believes engineers will use unstructured time productively. And frankly, sometimes that scepticism is warranted — this type of work is subjective and hard to measure. But the alternative is never addressing entropy at all, which is how codebases become unmaintainable.
Fast Feedback Loops
Fear is entropy’s best friend. When engineers are afraid to change code because they don’t know what might break, they default to the safest possible approach: changing as little as possible, working around problems rather than fixing them. Each workaround adds complexity. Fear begets entropy.
Fast tests, comprehensive observability, and quick rollbacks reduce fear. When you can deploy confidently, knowing that problems will be caught quickly and reverted easily, you’re free to make the changes that actually improve the system. When deployment is a high-stakes event that requires a change review board and a prayer, you’re going to avoid it — and everything that would require it.
Shared Understanding
This might be the most underrated factor in fighting entropy. Entropy grows fastest when teams don’t agree on what “good” looks like or when to stop adding complexity.
Code reviews become battlegrounds. Architectural discussions become political negotiations. Without shared aesthetic sensibilities, every decision is a debate, and the path of least resistance is always to just ship what works, elegance be damned.
Developing shared taste takes time. It requires pairing, discussion, retrospectives on what went well and what didn’t. It means having explicit conversations about coding standards that go beyond formatting rules into questions of design. It’s slow work, but teams with shared taste make better decisions faster and produce more coherent systems.
A Rule of Thumb
If I could distill everything above into one sentence, it would be this:
Entropy increases at the speed of your delivery unless you invest proportionally in clarity.
Fast-moving teams aren’t entropy-free. They’re just better at constantly paying it down. They refactor as they go. They delete aggressively. They maintain fast feedback loops. They’ve built a shared understanding of quality that makes good decisions the default.
As the author Anne Lamott wrote about writing — but could have been describing software — “Almost all good writing begins with terrible first efforts. You need to start somewhere.” The same is true for codebases. They all start messy. The question is whether you’re actively making them better or passively watching them decay.
The Bottom Line
Code entropy isn’t a bug to be fixed. It’s a force to be managed, like gravity or technical debt’s more charismatic cousin. You can’t eliminate it, but you can understand it, measure its symptoms, and build practices that counteract its effects.
The teams that thrive aren’t the ones that write perfect code — those teams don’t exist. They’re the ones that have made peace with entropy’s inevitability while building the discipline to fight it anyway. Small refactors every day. Deletion as a first-class activity. Clear ownership evolving into shared responsibility. Protected time for healing. Fast feedback that enables courage. Shared taste that makes good decisions natural.
Your codebase is dying. That’s not a criticism — it’s physics. The only question is whether you’re going to manage that decay thoughtfully or wake up one day wondering why everything takes so long.
Now, if you’ll excuse me, I need to go delete some code. There’s a feature flag from 2021 that’s been “temporary” for long enough.