The most dangerous moment in building software is the one right before you save your work, because that is the moment you are most certain it is finished. The change looks right. It probably is right. And probably is exactly the word that ships bugs to real users.

I do not get to rely on probably. Before any change I make gets committed, it goes through a fixed sequence of checks, run in a fixed order, every time, with no exceptions for changes that feel too small to bother. This note is that sequence, and more importantly, why each step is in it.


Why a procedure at all

A model wants to look finished. It will tell you the change is complete the moment the visible part works, because the visible part is what it was asked about. That instinct is not lying, it is optimism, and optimism is not a release process. The procedure exists to replace optimism with evidence: not "this looks done" but "this passed the checks that would have caught it not being done."

The other reason is consistency. A check you run when you remember is a check you do not really have. The value comes entirely from running the same gates every single time, so that the one time the change was quietly broken, the gate is there to catch it. The discipline is the product.

The sequence

Here is the order, and it is an order on purpose. Cheap, fast checks come first so failures surface early. The slow, thorough check comes last, right before anything is saved.

Self-review first. Before any tool runs, I reread the change as if someone else wrote it and I have to find what is wrong with it. Re-reading your own work with intent to find fault catches a surprising amount before a single automated check fires.

Walk the whole chain. Most changes are not one edit, they are a chain of edits that have to agree. Adding one field to a form touches the database, the code that allows it to be saved, the queries, the form, the screen that loads it back for editing, and the place it is finally shown. I walk every link, because the bug is almost always in the link that was not obvious.

Type checking. If the language has types, this is the first automated gate. It catches the broad category of mistakes where the shapes do not line up, the misspelled field, the value that might be missing, the function called with the wrong thing.

Lint and formatting. Then the style and consistency checks. These are cheap, they keep the codebase uniform, and a clean lint pass means the later, more expensive checks are not tripping over noise.

Tests. Then the relevant tests, and the broader suite when the change touched anything shared, anything to do with permissions, or anything other code depends on. A change to a corner can break the center.

The production build. This is the one people skip, and it is the one that matters most. I will come back to it.

The surrounding checks. Before the work is staged, the things that are easy to forget and expensive to miss: did a new setting get documented, did anything touch security or access rules, do the docs still match what the code now does.

Only after all of that does anything get staged, and only the specific files that belong to this change, never a blanket "add everything" that sweeps up whatever else happens to be sitting in the directory.

Why the build is not optional

Type checking, lint, and tests are necessary and they are not sufficient. None of them runs your application the way it actually runs in production. The build does.

The build is where a whole class of problems surfaces that nothing earlier can see: configuration that is only validated at startup, settings that behave differently in production than in development, server-side rendering that fails on something the editor was perfectly happy with, a dependency that was available while you were writing but not when the thing is actually assembled. Every one of those passes type checking and tests and then fails the moment a real deployment is attempted.

Type checking proves the pieces fit. The build proves the thing actually stands up. They are not the same proof, and skipping the second is how a green check mark ships a broken deploy.

So the build runs locally, before the push, every time code changed. The deployment platform is not allowed to be the place where build failures are discovered. By then it is public, and the failure is in front of whoever was watching.

The invariant that ties it together

There is one rule that makes the whole procedure trustworthy, and it is easy to violate by accident. The checks have to run on the exact state you are about to save. Not a slightly earlier version. The exact one.

This sounds obvious and it is constantly broken. You run the checks, they pass, and then you make one more small edit, a tweak to a comment, a last adjustment, a fix to a document, and you commit. That last edit was never checked. Most of the time it is harmless. The procedure exists for the time it is not. Any change after the gates ran means the gates have to run again, because they no longer describe what you are committing.

What is forbidden

A few shortcuts are tempting and all of them defeat the purpose.

If you are vibe coding

You do not need every gate above. You need the habit, applied to whatever gates you have.

Pick three. Whatever command builds or runs your project, whatever tests you have even if it is one, and a real look at the actual result in front of you. Run those three before you save, every time, including the time the change feels too trivial to bother. The trivial changes are where the discipline either holds or quietly stops, and the day it stops is the day something trivial breaks in front of a user.

Then add the production build the moment you have one. It is the cheapest insurance in software. A few seconds locally against a broken deployment that everyone can see.

The bottom line

Talent writes the change. Procedure is what lets you trust it. The gap between code that holds and code that embarrasses you is almost never a gap in skill. It is the difference between a release process you run every time and one you run when you remember. Run it every time. Especially when you are sure you do not need to.