invariants before tools
file: journal/invariants-before-tools.md
title: invariants before tools
date: 2026-07-29
tags: design, method
blurb: Every project has a small number of things that must never be untrue. Find those first and the stack stops being a decision.
---
invariants before tools
Ask what a system is built with and you get a list: the language, the framework, the database, the queue. Ask what must never be untrue about it and most teams go quiet, then answer with something that turns out to be a preference rather than an invariant.
The difference is testable. An invariant is something that, if it stops being true, means the system is wrong even though nothing crashed. A preference is something that, if it stops being true, means somebody is annoyed.
an example, small enough to hold
A gallery site for a painter. The obvious requirements arrive first: pages for the works, an admin panel to add them, decent typography, a contact form.
None of those are invariants. These were:
- Withdrawing a work removes it everywhere at once: from the pages, the cache, the feed, etc.
- Publishing is all-or-nothing. There is no state in which half the new exhibition is live.
- The images outlive the site. If every line of code is deleted, the work is still on disk in its original resolution, in a directory a human can read.
Notice what those three do to the design before a single technology is named. The first rules out any cache the publishing step cannot invalidate synchronously. In practice that means the publishing step must own the cache rather than politely notify it. The second gives you the atomic swap. The third says the images are not allowed to live only inside a CMS's storage abstraction, which quietly disqualifies most CMSes.
Three sentences, and the shape of the system is already decided. The stack is now a consequence: static output, a build step that owns the whole output directory, images stored as files under names a person chose.
why this order and not the other
The reverse order, tools first, feels faster because tools are concrete and invariants are not. You can install a framework this afternoon. You cannot install a decision about what must never be untrue.
But the cost arrives later and is charged with interest. When you pick the tool first, you inherit its invariants, and its invariants were written for someone else's problem. Every requirement of yours that conflicts with one of them becomes a workaround, and the workarounds accumulate until the system's real behaviour is documented nowhere except in the scar tissue.
I have watched this happen in software, and I have watched the identical thing happen with machinery on a farm. Buy the implement first and you spend the season adapting the field to it. Establish what the field requires and the implement chooses itself. The one that chooses itself is usually smaller, older and cheaper than the one you would have bought.
how to find them
Three questions, asked about the thing you are actually building rather than about systems in general:
- What would make a user say this is broken even though nothing threw an error? That is usually an invariant, stated in the negative.
- What is the worst thing that can be true for one second? If the answer is nothing much, you have found a place where eventual consistency is genuinely fine, and those places are worth knowing too.
- What must survive the deletion of this codebase? Data, formats, URLs. Those are invariants about the world, not about the program, and they are the ones people forget to write down.
Write the answers as sentences, in a file, where the next person will find them. Tickets and tests come later. A test is an invariant made executable, which is only possible once the invariant has been written down.
the part that keeps
Tools change on a cycle of a few years and always will. The invariants of a gallery site have not changed since the first gallery: the withdrawn work stays withdrawn, the publish is all-or-nothing, the images outlive the code. They will read exactly the same in twenty years.
That asymmetry is the whole argument. Time spent on the layer that changes fastest is time you will spend again. Time spent on the layer that stays is the only kind that accumulates.
resultFind what must not change. Then make everything else cheap to change. In that order.