I Kept It Small So It Couldn't Turn On Me
Starting with four browser extensions. Two free, one for sale, and one sitting in a review queue that may well have cleared by the time you read this.
That is everything I have shipped. It is not much of an empire. What I did not expect is the thing that arrived with them, which was not money and was not praise. It was a standing appointment.
Firefox puts out a new version, so I go and test. Chrome puts out a new version, so I go and test. Safari the same. Nothing in my code changed on any of those days. The ground underneath it did.
Nobody mentions this part. Shipping gets described as a finish line, and it is closer to a subscription: you keep paying for as long as the thing exists, and the bill arrives whenever a platform you do not control decides to move.
So here is the honest version of where I stand, which is also the reason this post exists. My code has not turned against me. That is not because I am careful or good. It is because I kept every project small enough that it could not. Four extensions, each doing one thing, on purpose. I shelved a much bigger idea because it was more than I could carry, built things I could actually finish, and shipped those instead.
I am working back toward that bigger idea now. Which means giving up the protection.
These six words describe what waits on the other side of it. They run from the smallest thing to the largest: one value, the path it travels, how the pieces are joined, how many copies exist, what that costs, and how you dig out. A convenience rather than a law. Most people meet them in whatever order their own project decides.
1. State
What your program remembers between one moment and the next. Anything it is holding onto: what you clicked, what got loaded, whether it is halfway through something.
The classic symptom is code that works the first time and not the second, and then works again after you restart it. Nothing changed except what the program was still carrying from last time.
Extensions hold very little state, which is much of why they are forgiving and why I suggest them as a first build. A real application has state everywhere, and every piece of it can be wrong without looking wrong.
Turn it into a prompt: "What is this holding onto between runs? What happens if it is still there when I do the next thing?"
2. Data flow
Where a value comes from and where it goes. A number shows up on the screen. Which part of the code decided it, and what did it pass through on the way?
On something small you can hold the whole path in your head, and that is what lets you fix things rather than guess at them. On something large you cannot, and if you did not write it you never built the map at all. That is the specific way generated code is harder to live in. Not worse. You just have no memory of it.
Turn it into a prompt: "Trace this value for me. Where does it come from, what touches it, where does it end up?"
3. Coupling
How much else has to move when you move one thing. The textbook phrasing is the degree of interdependence between parts. The practical version is whether change is cheap or expensive, and it is the single best measure of how a project is aging.
Mine is loose by accident rather than virtue: four small things that do not know about each other cannot tangle. But there is one kind I do have, and it is the one that generates the standing appointment. I am tied to three browser platforms I do not control. When Chrome moved everything to a new extension format, that was not a bug in anything I wrote. It was the far end of a connection I did not choose, deciding to move. Every dependency you accept is a small version of that: someone else can now make work for you.
Turn it into a prompt: "If I change this one thing, what else has to change with it?"
4. DRY
Don't repeat yourself. The same logic should live in one place, so that fixing it fixes it everywhere.
Models duplicate by default, and not out of laziness. Copying the working block into a second place always works, immediately, with no risk to the first one. It is the locally correct move every single time, and doing it repeatedly is how the same decision ends up written out in four places.
The symptom to watch for: you fix something, you are pleased, and the same problem surfaces elsewhere a week later. That is usually not a new bug. That is the second copy.
Turn it into a prompt: "Does this logic already exist somewhere else in the project? Show me every copy."
5. Technical debt
The running total of every "good enough for now." The metaphor is Ward Cunningham's, and the important half of it is the part people skip: debt charges interest. The shortcut is not the cost. The cost is every future change being slightly harder because of it.
Here is my admission for this post. I do not have much technical debt, and it is not because I am disciplined. It is because I have not built anything large enough to accumulate any. That is a real distinction and I would rather name it than let you assume otherwise. Discipline gets tested at scale, and mine has not been.
What I can tell you is that this is the term that accrues fastest with AI, because the interest is invisible and the loans are tiny. Every "it works, moving on" is one.
Turn it into a prompt: "What did we do the quick way here, and what will it cost me later?"
6. Refactor
Changing the structure of code without changing what it does. The definition carries a clause: without changing its observable behaviour. That clause is the entire word, and it is the part that gets broken.
Ask a model to tidy something and it will often improve it and alter it in the same pass, because from the inside both look like helpfulness. The tidying is real. The alteration is silent. With no way to check that behaviour held, you have not refactored anything. You have rewritten it and hoped.
Which is why this word comes last. It is the way out of the other five, and it is only safe once you can tell whether the thing still does what it did.
Turn it into a prompt: "Restructure this without changing what it does, and tell me exactly how I can confirm the behaviour is identical."
The idea I put down
It is still sitting there. I go back to it now and then, look at what it would take, put it down again, and each thing I finish in the meantime makes the pile look slightly smaller.
When I do build it, all six of these are what I will be dealing with, in a project big enough to punish me for getting them wrong. Which makes this a strange post to have written: vocabulary for a stage I can see coming and have not properly arrived at, published in advance rather than in hindsight. I would rather have the words before I need them. That has been the point of the whole series.
Guardrail #8
Count what you will maintain, not what you will build.
Before adding a feature, ask who checks it when the platform underneath it changes. If the answer is you, and you do not want that appointment, do not build it.
That closes the series on the words that change what the AI hands back: before you type a prompt, once the code comes back, before you run it for real, when it fails without saying so, and now what it costs to keep the thing alive afterward.
Receipts
- Technical Debt · Martin Fowler, crediting the metaphor to Ward Cunningham's 1992 experience report
- Definition Of Refactoring · Martin Fowler, on the "without changing its observable behavior" clause
- Coupling · the degree of interdependence between software modules
- Migrate to Manifest V3 · Chrome for Developers, the platform change that made work for every extension author who had already finished
Comments
Post a Comment