Shipping discipline: small checks prevent big failures
Speed without discipline produces technical debt, not velocity. The practices that help me move fast safely aren't complex—they're consistent. Most of them take minutes to set up and save hours (or days) of firefighting later.
The best debugging session is the one you never have to do.
What "shipping discipline" means to me
It's not about heavy process or ceremony. It's a small set of habits that keep the codebase in a state where you can deploy confidently at any point:
1. Reproducible environments
"It works on my machine" is the most expensive sentence in software. I use:
- Lock files for every dependency (
package-lock.json,poetry.lock,Cargo.lock). - Containerised development environments when feasible.
- Explicit version constraints—no floating versions in production code.
The goal is that anyone (or any CI runner) can check out the repo and get identical behaviour.
2. Dependency hygiene
Every dependency is a liability. I audit what comes in, prefer well-maintained libraries, and keep the dependency tree as shallow as practical. Regularly updating dependencies is cheaper than dealing with a year's worth of accumulated security patches at once.
3. Automated checks that run on every push
A minimal CI pipeline doesn't need to be complicated. The baseline I set up on every project:
- Lint and type-check pass
- Tests pass (unit at minimum, integration where practical)
- Build succeeds
- No secrets or .env files committed
This catches the majority of "obviously broken" commits before they reach anyone else. It takes 10 minutes to set up with GitHub Actions and saves countless hours.
4. Clear "definition of done"
A feature isn't done when the code compiles. It's done when:
- It works as specified (tested, not just eyeballed).
- Edge cases are handled (or explicitly documented as known limitations).
- It doesn't break existing functionality.
- Someone else can understand what was changed and why (commit messages, PR descriptions).
5. Good error handling
Errors are information, not annoyances. I prefer:
- Errors that explain what went wrong and where, not just "something failed."
- Structured error types over generic exceptions.
- Fail-fast where appropriate—surface problems immediately rather than letting them cascade.
- Logging that's useful in production, not just in development.
6. Predictable releases
Semantic versioning, tagged releases, and changelogs aren't bureaucracy—they're communication. When something breaks in production, knowing exactly what changed between versions cuts investigation time from hours to minutes.
The cost of skipping these
Every shortcut here is paid back with interest:
- No lock files → "why does production behave differently?" debugging sessions.
- No CI → bugs discovered by users instead of tests.
- No error handling → hours spent tracing silent failures through log files.
- No release discipline → "which version is running?" guessing games during incidents.
Applying this in practice
In recent work where speed is critical, the temptation to skip "process" is strong—but the practices above aren't process. They're engineering hygiene. They take minutes to set up and they increase speed by reducing the time spent on avoidable problems.
In recent work, maintaining CI checks, dependency pinning, and clear release tags helped deliver working, deployable software—not just code that "should work."
Takeaway
Shipping discipline isn't about moving slowly. It's about building the small guardrails that let you move fast safely. The best teams I've worked with aren't fast despite their process—they're fast because of it.