Orthogonality in Practice
Orthogonal components don't affect each other. Change one, and nothing else needs to move.
That sounds abstract until you notice it has a direct metric: how many files does a typical one-line behavior change touch?
The diff test
Add a field to a form. Count the files.
- 2-3 files: healthy. The change lives where the concept lives.
- 8+ files: the concept is smeared across the system. A form field shouldn't require edits to a validator registry, a DTO, a mapper, a migration, a serializer, a fixture, and three tests that assert shape rather than behavior.
The second case isn't caused by bad code. Every one of those files is reasonable in isolation. It's caused by a layering scheme where one idea has to be re-declared at every level.
Where non-orthogonality hides
Shared mutable config. A settings object every module reads means changing a default silently changes six behaviors.
Inheritance used for reuse. A base class with eight subclasses is eight things you might break by editing one method.
Cross-module test fixtures. One shared testUser fixture, and adding a required field breaks 40 unrelated tests. The tests are now coupled to each other rather than to the code.
The cheap fix
Most of the recovery is boring: pass dependencies in rather than reaching for them, prefer composition, and give each test its own data.
The payoff isn't elegance. It's that the blast radius of a mistake stays small enough to reason about, which is the only thing that makes a large codebase survivable.
Previous
← Tracer Bullets Beat Prototypes