It Isn’t Spaghetti

Every change passes through six gates. 01 Diagnose current state 02 Specify precisely 03 Propose one edit 04 Read it and explain it 05 Verify against prediction 06 Stage per file, commit If I cannot explain it, it does not land. PreShiftIQ

The objection is not that a non-developer could not build a platform. It is that whatever he built must be a mess underneath. That objection is reasonable.

The objection I hear most is not that a non-developer could not build a platform. It is that whatever he built must be a mess underneath.

That objection is reasonable, and I want to take it seriously rather than wave it off, because the people raising it are usually right about the general case.

Here is what is true in it. A model will produce something plausible for almost any request you make, and plausible is not the same as correct. It will happily generate a function that runs, passes a shallow test, and quietly does the wrong thing at the edges. It will write code that works today and creates a problem eighteen months from now that nobody can trace. If you accept that output at volume without reading it, you end up with a codebase nobody understands, including you, and that is genuinely worse than not having built anything, because now other people depend on it.

So the question is not whether AI can write good code. The question is what governs what actually lands.

The code is not the control. The process is.

The rules are written down, and the first one is the one everything else hangs on. This platform is being built to enterprise grade, and time is not a currency I spend against that. No shortcuts. No temporary fixes. No pragmatic-for-now version that we will come back to, because we never come back to it. If a thing takes four times longer to build properly, it takes four times longer. Effort is not a cost I weigh, and that single rule eliminates most of the decisions that would otherwise degrade a codebase over time, because the tempting option is almost always the fast one.

Every session begins with diagnostics, and this is not negotiable. Before any work starts, current state gets verified against reality. What is actually in the repository. What the database actually contains right now. What is actually deployed. Never what I believe I left behind, because what I believe is a memory and memories drift. This one rule has caught more problems than any other on the list, and it costs fifteen minutes.

Per-edit approval, never allow-all. Every change is proposed to me one at a time and I approve or reject each one. There is a setting that would let it run unattended and I have never used it. Unattended means volume, and volume means changes I did not read. The day I accept an edit I cannot explain is the day the codebase stops being mine.

Per-file staging, never a blanket add. Git has a command that stages everything in one shot and it is the default habit of most people who use it. I stage file by file, which forces me to look at every single file entering a commit. It has caught generated artifacts, stray debug output, and once a credential that had no business being in version control.

Stop and confirm before every commit and every push. No exceptions, including on changes I am certain about. The point is to make committing a decision rather than a reflex, because reflexes are how things arrive in production that nobody chose to send.

Diagnostics before action, every time. Before any schema change or any SQL that touches data, I prove the current state against the database rather than assuming it. A confident wrong assumption about state is the most expensive kind of mistake, because the change works perfectly against the state you imagined and destroys something in the state that actually exists.

Predicted counts before verification. This is the one I would keep if I could only keep one. Before running a search to confirm a change landed correctly, I state out loud how many matches I expect to find. Then I run it. If the number comes back different, I stop, because the mismatch does not mean the search was wrong, it means my understanding of the system was wrong. Every other check on this list catches bad syntax. This one catches bad comprehension, which is the failure that actually produces spaghetti.

Migrations local first, verified, then production, never automatic on deploy. Automatic migration on deploy means a schema change ships silently alongside a code change and you learn about the problem from production. Every migration is applied locally, verified against the local database, and then applied deliberately to production as its own decision.

Anything disabled is tracked until it is re-enabled. When something gets turned off to unblock work, that re-enablement becomes a hard requirement before the work is allowed to close. Temporary is the most permanent state in software, and the only defense is refusing to let the disabling work finish while it is still disabled.

None of that is sophisticated. It is deliberately boring, and boring is the point.

Now the part where the discipline earned its keep.

There was a day in May when it felt like everything was failing at once. I ran a full user acceptance pass against production and came out of it with roughly twenty defects. Some were cosmetic, some were not, and a couple were the kind that make you question whether the thing works at all. I was irritated, and my instinct was to start fixing them immediately, in the order I had found them.

I called a full stop on the build instead.

We spent hours doing nothing but diagnostics. No fixes, no commits, just working through every defect to answer one question: is this an instance, or is this a class? Twenty symptoms is not twenty problems. Several of them turned out to be the same underlying issue surfacing in different places, and fixing them individually would have meant fixing the same thing four times and still leaving the cause in place.

That triage produced fifteen tickets from the original twenty findings. Then the deep dive kept going, and it surfaced thirteen more issues that had not been in the original twenty at all. Some were architectural. One was a case where my own mid-session diagnosis had been flatly wrong: I had assumed a webhook authentication problem, and the evidence showed the failures were coming from my own application logic rejecting duplicate records, which is a completely different bug with a completely different fix. If I had gone with my instinct, I would have spent the day solving a problem I did not have.

Another one only surfaced because a structural fix moved where a piece of code executed. Two services had been running with different credentials for weeks, and the old architecture had hidden it perfectly, because the affected code only ever ran in the service that happened to be configured correctly. Restructuring it surfaced the mismatch immediately. The fix was a latency improvement on paper and a latent-misconfiguration detector in practice.

If I had patched the twenty, I would have shipped twenty fixes and left thirteen problems in the ground, including one I would have been actively wrong about.

The frustration was the signal to stop, not the signal to go faster. That is the only useful thing I have learned about frustration in this entire build.

MIT's research on enterprise generative AI found that roughly 95% of pilots delivered no measurable impact on the P&L, and that tools built with external vendors succeeded about twice as often as internal builds. I am squarely in the category that usually fails, and I have no argument that I am special. What I have is a process that makes the common failure modes hard to reach, and a refusal to accept anything I cannot account for.

Here is the failure mode that would tell me this is not working, because a process that cannot fail is not a process, it is a slogan.

If I start approving edits because the explanation sounds right rather than because I followed it, the discipline is already gone and the audit trail will not catch it, because the audit trail records what happened rather than whether I understood it. The tell is the pace. When I am moving fast enough that I am pattern-matching on the shape of an answer instead of reading it, I stop for the day. That has happened, and it will happen again.

The codebase is not clean because AI wrote clean code. It is clean because nothing lands that I cannot defend.

Selecting supply chain technology?

PreShiftIQ matches buyers to vendors on measured fit across TMS, dock scheduling, ELD, carrier vetting, and fleet management. Vendors pay for outcomes, never for influence, and the buyer match is free.

Get your free match

See how matching works at preshiftiq.com/how-it-works.

Sources

  • MIT NANDA initiative, The GenAI Divide: State of AI in Business, drawing on 300-plus public deployments, 52 organizational interviews, and 153 leader surveys.

Next in the series: operators over academics, and why every translation hop between the person who knows the problem and the person who writes the code loses the part that matters.

Previous
Previous

Operators Over Academics

Next
Next

I Am Not a Developer