On software development checklists
After a diagnosis of a patient has been performed by a specialist, and a procedure, say a surgery; has been arrived at, a specialist must follow a well trodden path.
They set the parameters on what’s to be done, where, how and within which timeframe before, for example, the patient becomes emergent (regaining consciousness after anesthesia).
I have come to a conclusion that maybe software development should happen in the same way.
No, I am not talking about building an LLM wrapper that only has 5 API calls, and that’s it.
Overtime, I have found setting up the same things - linters, git hooks, scripts, etc over and over again (without hindsight)…
It could be argued that the time investment for these miniscule tasks is minimal but it’s exponential in a way. It drains the small reserve of attention you have when a project is brand new and every decision still feels consequential.
This is, I think, the real argument for a checklist. Not that it makes you faster, although it does.
It’s that it frees up the part of your brain that should be thinking about the thing only you can think about - the actual shape of the problem you’re solving - by offloading the parts that are functionally identical across every project you’ll ever touch.
Atul Gawande’s The Checklist Manifesto makes essentially this case for surgery. The argument isn’t that surgeons are incompetent without a checklist. It’s that competence under cognitive load is unreliable, and a list catches the boring failure modes - wrong site, unconfirmed allergy, missing equipment - before they become catastrophic ones. The surgeon isn’t smarter for using the list. They’re just no longer relying on memory for things memory was never well-suited to hold in the first place.
Below is a list of the parts that I normally have to set up or introduce in projects.
Linters, lint rules and formatters.
There’s something poignant about consistently formatted code.
(Not because the formatting itself matters - tabs versus spaces has never won anyone an argument worth having - but because a linter run on day one means style stops being a conversation. Nobody opens a PR three months in and asks why half the file uses single quotes. The argument was already had, once, by a config file, before there was any code to disagree about.)
Git hooks.
A pre-commit hook that runs the formatter and a pre-push hook that runs the fast tests. Not the whole suite - that’s what CI is for - just enough to catch the kind of mistake that’s embarrassing in review. The point of a hook isn’t enforcement of a particular style, it’s removing the need to remember context about for example, how long should commit messages be?.
Environment configuration and secrets.
An
.env.examplecommitted alongside the real.envthat never is. This is the one item on the list I’ve seen skipped the most, usually by people who are confident they’ll remember which seven variables the project needs. They won’t, and neither will whoever joins six months later.Some times, adding a
.env.examplegeneration command from a tool like Infisical is a huge quality of life improvement.A migration tool, decided before the first table exists.
Once a schema has been hand-edited in production, it will be hand-edited in production again. The fix isn’t discipline, it’s removing the option - migrations become the only sanctioned way the schema changes, full stop.
Structured logging, from the first log line.
Not because you need it on day one, but because retrofitting it on day ninety means going back through every print statement you ever wrote and deciding, after the fact, what was actually worth knowing.
A CI pipeline: lint, test, build
Deployment targets change. Hosting providers change. But the discipline of “nothing merges that doesn’t pass these three steps” shouldn’t have to wait for either of those decisions to be made.
Testing conventions
It could not be for a full project coverage but an explicit answer in regard to “what gets a test and at what layer,” decided once, so it isn’t re-litigated in every review.
A README that actually gets a new clone running.
One command, ideally. The test is simple: could someone with zero context get the project up without asking you a question. If the answer is no, the README isn’t done.
I have gotten onboarded onto projects where the owner had to call up a developer from over 2 years ago since I could not find something I wanted.
Dependency and vulnerability scanning
In the era of increasing software vulnerabilities and supply chain attacks, this is the kind of thing nobody thinks about until an advisory lands on a package three layers deep in the dependency tree, at which point you’re grateful it surfaced on its own.
None of these are interesting in isolation. That’s rather the point - a checklist isn’t supposed to be clever, it’s supposed to be boring and complete, so that the parts of a project worth being clever about get all the attention they’re due.