You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: content/posts/2025-09-07-the-only-meaningful-measure-of-code-quality/index.md
+66-24Lines changed: 66 additions & 24 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,51 +9,80 @@ showFullContent = false
9
9
hideComments = false
10
10
+++
11
11
12
-
We spend a lot of time talking about "code quality". Ask ten developers what it means, and you'll get ten different answers: low bug rate, consistent style, high unit test coverage, SOLID principles, low cyclomatic complexity, or maybe just "code that makes me feel good when I look at it", but when you boil it all down, there's only one measure of quality that really matters: **how easily we can change the code tomorrow**.
13
-
14
-
Everything else - readability, test coverage, architecture, even naming conventions—only matters in so far as it increases (or decreases) our ability to adapt the codebase when reality inevitably changes.
12
+
We spend a lot of time talking about "code quality". Ask ten developers what it means, and you'll get ten different answers: low defect rate, consistent styles, high readability, high test coverage, alignment to SOLID principles, deployable on commit, low cyclomatic complexity, using clean architecture, or maybe just "code that I like writing", but when you boil it all down, there's only one measure of quality that really matters, **how easily can we change the code tomorrow**, and everything else feeds into this metric and only matters in so far as it increases (or decreases) our ability to adapt the codebase when change is inevitably required.
15
13
16
14
---
17
15
18
16
{{<toc>}}
19
17
20
18
---
21
19
22
-
## Why change is the real measure
20
+
## Why Change Is The Real Measure
21
+
22
+
Software doesn't live in a vacuum. Requirements shift, features evolve, teams grow, technologies come and go, and mistakes need fixing. The one constant is change. If your code is beautiful but rigid, it's a liability. If it's ugly but malleable, that's a good start. If it's both beautiful and malleable, that's the place to be, and I would argue further that beauty is a side-effect of good malleability.
23
+
24
+
When it comes to code hygiene, some examples of common mistakes include ...
25
+
26
+
**Over-Engineering**
27
+
28
+
Future-proofing is important, but building abstractions and layers of indirection "just in case" can weigh a project down. Every extra generic, factory, or configuration knob becomes another thing to maintain. Trying to anticipate every future need adds unnecessary weight, and when the time comes to adjust you’ll find yourself dragging that weight along.
29
+
30
+
**Code Golf**
31
+
32
+
A clever one-liner might impress your peers, but if nobody can understand or debug it, or safely refactor it six months later, is it really high quality? That's rhetorical; the answer is "no".
33
+
34
+
**Muddle-Through Debugging**
23
35
24
-
Software doesn't live in a vacuum. Requirements shift, features evolve, teams grow, and mistakes need fixing. The one constant is change. If your code is "beautiful" but rigid, it's a liability. If it's ugly but malleable, it's an asset.
36
+
When faced with a defect, some developers scatter fixes everywhere without understanding the root cause. It might get the system working temporarily, but it leaves behind brittle and unpredictable code. Imprecise debugging makes future changes dangerous, because nobody really knows which line of duct tape is holding things together.
25
37
26
-
Think about it:
27
-
- A clever one-liner might impress your peers, but if nobody can understand or safely refactor it six months later, is it really high quality?
28
-
- A 90% test coverage report looks great on paper, but if the tests are brittle and block every small refactor, they're hurting change, not helping it.
29
-
- Strict adherence to patterns can feel disciplined, but if they're applied dogmatically, they can trap you in a maze of abstractions that makes change harder, not easier.
38
+
**Cargo-Cult Programming**
39
+
40
+
Strict adherence to patterns can feel disciplined but, if they're applied dogmatically and without a good measure of critical thinking, it can trap you in a maze of abstractions that makes change harder, rather than easier. To put it a different way, with an example inspired by a saying about fruit salads: knowledge means knowing that an empty interface has a perfect [dependency score](https://en.wikipedia.org/wiki/Software_package_metrics), while wisdom means understanding that an empty interface is an invalid contract.
30
41
31
42
---
32
43
33
-
## The illusion of secondary metrics
44
+
## The Illusion Of Preventive Metrics
45
+
46
+
We latch onto preventive metrics because they're easy to measure in the moment: linting results, test coverage reports, static analysis scores. These metrics provide valuable insights, but they measure process attributes rather than the outcome that truly matters: how easily the code can be modified.
34
47
35
-
We latch onto metrics because they're easy to measure: linting, test coverage, code churn, static analysis scores. But these are proxies, not the end goal. They can give us signals, but none of them guarantee adaptability.
48
+
For example:
36
49
37
-
For example:
38
-
-**Low complexity** often means easier changes—but not always. Sometimes a complex domain requires complex code, and over-simplifying it makes future changes *harder*.
39
-
-**Consistency in style** makes change less mentally taxing—but style guides alone won't save you from tangled dependencies.
40
-
-**Performance optimizations** may be technically impressive, but if they freeze the design and prevent iteration, you're paying the wrong price.
50
+
- A codebase with 100% test coverage might be thoroughly tested but painfully rigid. Every small change could break dozens of brittle, implementation-dependent tests.
51
+
- You can achieve perfect cyclomatic complexity scores by extracting every decision into its own method, but end up with a spaghetti of tiny functions that no one can follow.
52
+
- Code can pass every linter rule while still embodying a fundamentally poor design that resists change.
53
+
- Low coupling scores might hide the fact that your modules communicate through global state or rigid protocols that make real-world changes difficult.
54
+
55
+
These metrics are useful guardrails, but we shouldn't mistake them for the destination. I've seen many teams celebrate perfect static analysis scores while dreading any requirement change because they know how fragile their faux-perfect codebase actually is.
41
56
42
57
---
43
58
44
-
## Designing for change
59
+
## DORA Metrics As Reactive Signals Of Changeability
60
+
61
+
[DORA metrics](https://dora.dev/guides/dora-metrics-four-keys/) can serve as a dashboard of secondary effects produced by (or degraded by) the effort required to modify code. When a codebase is easy to change, deployment frequency rises naturally because changes can be sliced thin and deployed independently. Lead time for changes shrinks because developers spend less time navigating through incidental coupling. Change failure rate drops because modifications remain localized and their intent is clear. Mean time to restore improves because the system's behaviour is observable and its seams are explicit.
62
+
63
+
When any of these metrics trend in the wrong direction, they signal that hidden friction is accumulating within the codebase. Tests may have begun obstructing refactors instead of enabling them. Architectural boundaries might have become leaky. Abstractions could be defending past decisions rather than facilitating new ones. The value in these metrics isn't in chasing them directly, but in mining them for early warnings that the real asset, changeability, is eroding.
64
+
65
+
---
66
+
67
+
## Designing For Change
45
68
46
69
So, how do we actually measure and design for changeability? A few guiding principles:
47
70
48
-
1.**Localize decisions** – Changes should ripple as little as possible. If adding a feature forces edits across ten files in unrelated modules, your design is resisting change.
49
-
2.**Lean on tests that enable refactoring** – Tests should make it *safer* to change code, not punish you for trying. Favor higher-level integration tests over brittle mocks.
50
-
3.**Readability is for future-you** – You don't write code for the compiler; you write it for the humans who will modify it next (likely including yourself, after you've forgotten everything).
51
-
4.**Avoid premature optimization and abstraction** – Over-abstracted code makes small changes expensive. Over-optimized code is often calcified code.
52
-
5.**Practice continuous refactoring** – Small, ongoing cleanups keep the codebase supple. Waiting until it's "bad enough" usually means it's already too late.
71
+
1.**Localise Decisions** – Changes should ripple as little as possible. If adding a feature forces edits across ten files in unrelated modules, your design is resisting change.
72
+
73
+
2.**Lean On Tests That Enable Refactoring** – Tests should make it *safer* to change code, not punish you for trying. Favour higher-level integration tests over brittle mocks that freeze implementation details.
74
+
75
+
3.**Readability Is For Future-You** – You don't write code for the compiler; you write it for the humans who will modify it next (likely including yourself, after you've forgotten everything).
76
+
77
+
4.**Avoid Premature Optimisation And Abstraction** – Over-abstracted code makes small changes expensive. Over-optimized code is often calcified code.
78
+
79
+
5.**Practice Continuous Refactoring** – Small, ongoing cleanups keep the codebase supple. Waiting until it's "bad enough" usually means it's already too late.
80
+
81
+
6.**Measure The Right Thing** – Track how long changes take and why. When a "simple change" takes days, investigate the barriers that made it difficult rather than just accepting the cost.
53
82
54
83
---
55
84
56
-
## A mental shift
85
+
## A Mental Shift
57
86
58
87
When we review code, we should stop asking "Is this correct?" or "Does this look nice?" Those matter, but they're secondary. The real question is: **"If requirements change tomorrow, how painful will it be to adapt this?"**
59
88
@@ -67,5 +96,18 @@ Code quality is often treated like an aesthetic judgment or a scorecard of best
67
96
68
97
In the end, our job as developers isn't just to write code that works. It's to write code that *keeps working, even as the world around it changes.*
69
98
70
-
## TODO
71
-
go forth and nerd out
99
+
## A Mental Shift
100
+
101
+
Code reviews often center around concerns which address the code only as it exists today. The more critical matter, and the one that should guide our evaluations, is: **"When requirements inevitably change, how painful will it be to adapt this code?"**.
102
+
103
+
This shift in perspective transforms how we evaluate and write software. It prioritises clarity over cleverness, appropriate abstractions over rigid frameworks, and thoughtful design over mindless adherence to patterns. Changeability becomes the north star of code quality by which we navigate all technical decisions.
104
+
105
+
---
106
+
107
+
## Conclusion
108
+
109
+
We too often mistake the aesthetics of code for its quality, celebrating elegant solutions that become tomorrow's maintenance nightmares. We confuse adherence to best practices with actual value delivery. But genuine code quality has a simpler measure: **changeability**.
110
+
111
+
Can the code be safely extended when new features arrive? Can it be refactored without cascading failures? Can it evolve alongside shifting business needs? If yes, that code is high quality, regardless of whether it follows every pattern in the book. If not, no amount of syntactic sugar can redeem it.
112
+
113
+
The mark of professional software development isn't creating code that works today. It's creating code that continues to serve its purpose as the world around it transforms, code that bends but doesn't break under the constant pressure of change.
0 commit comments