There is a word for code that is written, reviewed, merged, and sitting in a branch waiting to go out: waste. It is not delivering value. It is not being tested by real users. It is accumulating risk — merge conflicts, stale dependencies, assumptions that were true last week but are not true today.

Good engineering ships. Not recklessly, not without thought, but continuously and deliberately.

The cost of not shipping

Every day a feature sits undeployed, it gets more expensive. The code drifts from the codebase around it. The engineer who wrote it moves on to other work and loses context. Other changes land that conflict with it. By the time it finally goes out, it requires a second round of work just to integrate.

This is the concept of inventory in lean manufacturing, applied to software. Work in progress that is not in production is a liability, not an asset. It ties up capacity, obscures risk, and delays feedback.

The teams that deploy once a quarter are not being careful. They are accumulating risk into a single terrifying release event. The teams that deploy ten times a day are not being reckless. They are making each individual change so small that it is almost impossible for it to break anything, and trivially easy to roll back if it does.

Small batches

The intuition that big releases are safer is exactly backwards. A deploy that contains three months of changes from twenty engineers is a nightmare to debug when something goes wrong. A deploy that contains one small change from one engineer is obvious to diagnose.

Small batches give you:

  • Faster feedback — you learn whether your change works in hours, not months
  • Easier debugging — when something breaks, you know exactly what changed
  • Lower risk — the blast radius of any single deploy is tiny
  • Continuous progress — stakeholders see steady movement, not long silences followed by a big bang

The trick is to decompose work into pieces that are independently shippable. Not every piece needs to be user-visible. A database migration can ship on Monday, the API endpoint on Tuesday, and the UI on Wednesday. Each deploy is boring. That is the point.

Feature flags and incremental rollouts

“But we cannot ship half a feature” is the most common objection, and it is almost always wrong. Feature flags let you merge and deploy code that is not yet visible to users. The code is in production, being exercised by your test suite and your infrastructure, but it is behind a flag that you turn on when ready.

This separates deployment from release. You deploy continuously — every commit that passes CI goes to production. You release deliberately — the feature becomes visible to users when you decide, to the audience you choose, at the pace you control.

Incremental rollouts take this further. Show the feature to 1% of users. Watch the metrics. If everything looks good, go to 10%, then 50%, then 100%. If something breaks, you turn off the flag. No rollback, no hotfix, no incident. Just a configuration change.

“Done” means in production

This is a cultural statement as much as a technical one. A feature is not done when the code is written. It is not done when the PR is approved. It is not done when it is merged to main. It is done when it is in production, being used by real people, and the metrics confirm it is working.

Everything before that is work in progress. Treat it accordingly. Track it, limit it, and push it toward production as fast as you responsibly can.

The goal is not speed for its own sake. The goal is learning. Every deploy is an experiment. Every feature in production is a hypothesis being tested. The faster you ship, the faster you learn, and the faster you learn, the better your product gets.

Ship small. Ship often. Ship today.