When Clever Code Costs a Sprint: A Quarterly Boring-Code Audit
Photo: overcomplicated code diagram on whiteboard with sticky notes in office, via www.theolivepress.es
Most teams have a file everybody avoids. It isn't necessarily bad. It may be the most technically impressive code in the repository. That is the issue. It was written by someone on a good day, and now it is maintained by people on a normal Tuesday.
Call it the cleverness tax. You pay it every time someone has to trace a callback through three factories to find the line that formats a date. You pay it in review comments, onboarding time and the quiet decision to leave a bug alone because fixing it feels risky. The tax is rarely visible in a sprint plan, but it is very visible when a small change takes three days.
A quarterly boring-code audit is a way to make that tax visible without turning the team into a refactoring cult. It is not a hunt for bad developers. It is a hunt for code that is more complicated than the problem it solves.
The audit is not a code review
A code review asks whether a change is safe and sensible. An audit asks a different question: what does this part of the system cost us to keep? That means looking at a handful of hotspots rather than every pull request. It also means accepting that some clever code is fine. If a dense regular expression has a clear comment and a solid test, leave it alone. The audit is for cleverness that has become a maintenance burden, not cleverness that has been properly contained.
Keep it small. Once a quarter, book 60 to 90 minutes. Pick six to ten hotspots using evidence you already have: files with frequent bug fixes, modules that slow down onboarding, parts of the codebase people mention in retros, or areas where every change seems to touch five files. If your team has a shared document for onboarding notes, read it. The complaints are usually already written down.
Score the friction, not the cleverness
A simple scoring sheet keeps the conversation honest. Score each hotspot from 1 to 3 on a few practical signals. A 1 means it is fine. A 3 means it is costing real time. Add the scores and discuss anything that lands high. Do not turn this into a precision measurement exercise. The point is to start a useful conversation, not to produce a dashboard nobody reads.
| Signal | What to ask | Score |
|---|---|---|
| Indirection | Does a simple change require touching more than three files? | 1 to 3 |
| Naming | Do names describe future possibilities rather than current behaviour? | 1 to 3 |
| Density | Would a colleague need a console or a whiteboard to understand the happy path? | 1 to 3 |
| Configuration | Is there config for a decision nobody has actually made? | 1 to 3 |
| Test shape | Do tests assert implementation details rather than behaviour? | 1 to 3 |
Two people can score independently and compare notes. The disagreement is often more useful than the number. One person may find a module elegant because they wrote it. Another may find it exhausting because they had to fix a booking bug in it on a Friday afternoon.
Four smells that keep turning up
The first is the abstraction for a second customer who never arrived. A notification service grows channel adapters for email, SMS, push and probably carrier pigeon, but the product only sends email. The extra layers are not free. Every new developer has to learn the shape of a future that is not here yet.
The second is the one-line wonder. A dense reduce or a chain of array methods can save four lines and cost four minutes every time somebody reads it. That is not a good trade in a codebase maintained by a team of six. Clear loops and named intermediate variables are deeply unfashionable and deeply useful.
The third is the framework around the framework. An internal wrapper sits over a date library, then another wrapper sits over that wrapper because the first one was too rigid, and now there are two date libraries and three ways to format a Thursday. Indirection should earn its place by hiding a real, stable complexity. If it only hides a function call, delete it.
The fourth is the configuration maze. A small service has feature flags for choices nobody changes, environment variables for values that are the same everywhere, and a settings object with more fields than the actual product. This is not flexibility. It is decision debt. A UK bookings service for a regional theatre does not need a pluggable pricing strategy until it has a second pricing strategy.
Run the hour without turning it into a trial
Start by naming the cost in plain language. Not this is bad code, but this makes the Christmas rota change take an extra day. Then describe what dull code would look like. Usually it is a function with a clear name, a couple of straightforward conditionals and a test that checks the outcome. Finally, write down the risk of changing it. If the risk is high, the first task is not a rewrite. It is a test.
Choose at most two items to fix this quarter. More than that and the audit becomes a wish list that nobody delivers. The rest go on a short list with a sentence explaining why they were left alone. That sentence matters. It stops the same discussion reappearing in three months.
Refactor towards boring on purpose
A boring refactor has a simple shape. First, add tests around the behaviour you are keeping. If the tests are hard to write, that is useful evidence in itself. Second, write the plain replacement alongside the clever code. Do not wrap the old abstraction in a new abstraction to make the migration easier. Third, move call sites over one pull request at a time. Fourth, delete the old code in the same quarter. A half-finished simplification is just a second way of doing things.
Keep the note short. A paragraph in the README or an architecture decision record is enough: we replaced the generic notification pipeline with three functions because we only send email and the pipeline made every copy change slower. That is the kind of history a future maintainer can use.
Make boring a compliment
The cultural bit matters more than the scoring sheet. In retros, recognise deletions and simplifications as real delivery work. In reviews, ask whether something can be simpler before asking whether it can be more generic. When someone removes a layer and the tests still pass, say thank you. Boring code is not a failure of ambition. It is often the result of ambition being pointed at the right target: a system that ordinary people can change on an ordinary day.
Book the next audit for the first week of February 2027. Keep the sheet in the repository, keep the meeting short and keep the output small. One fewer clever bit per quarter is a good year.