The problem
Most teams do not have a shipping problem. They have a batch size problem. This playbook, How to ship small changes to production more often, starts there. A release with six features, three fixes and a database change is hard to test, hard to review and hard to undo. The fix is not to push harder. It's to make each change small enough to understand and cheap enough to reverse.
Large changes create waiting. A branch lives for two weeks and collects merge conflicts and review comments. The reviewer sees a wall of diffs and approves the parts they recognize. QA gets a build that differs from what developers tested. Operations gets a release note that says misc fixes. Each one is a sign that the change was too big to reason about.
Shipping often gets confused with releasing often. A deploy puts code on servers. A release exposes a behavior to users. If you tie them together, every deploy becomes a business decision. That's why teams wait for a monthly window, then spend a week cleaning up. Small changes need a way to sit in production without being visible, and a way to be removed without a rebuild.
What a good solution looks like
A good solution treats a change as a unit of work with one reason to exist, one owner and one rollback path. It can touch many files if it's a rename, but it should make one behavior change. Refactors go in their own change. Database migrations use an expand and contract pattern so old and new code can run together. If you can't describe the change in two sentences, split it.
Deploy and release are separate steps. Code goes to production behind a feature flag, a config value or a dark route. The release is a separate action with its own approval. Internal tools can deploy from main on every merge. Customer facing apps can use a daily release slot and on demand releases for urgent fixes. The goal is not continuous deployment for its own sake. It's a steady flow of changes that are cheap to verify and cheap to undo.
The process should feel boring. A pull request gets one reviewer plus automated checks. The pipeline builds an artifact once and promotes it. A release captain watches the queue, not the code. The checklist is short: what is changing, how is it hidden, who owns the flag, what does rollback look like, and what will you watch after release. If a meeting is needed to approve a normal change, the change is too big or the decision rights are unclear.
Teams that ship often keep a ready queue. Work that is coded but not released sits in one visible place. That queue is the real constraint, not the deploy button. When it is empty, people pull the next small change. When it is full, they stop starting work and help finish what is there.
How to build it
Start with a written change size rule. One behavior change per pull request. No unrelated refactors. Split schema changes into expand, migrate and contract. A pull request template asks four questions: what user visible behavior changes, how is it hidden, what happens if we revert it, and which flag or config controls it. Reviewers can reject a change for size alone, and that rule applies to senior engineers too.
Set up a release queue board with columns for ready, blocked, in production hidden and released. Cap open pull requests per engineer. Two is a good starting point. The cap keeps people from starting work they cannot finish. Hold a short release review twice a week. Walk the blocked column first. Most blockers are missing test data, unclear ownership or a dependency that should have been split out.
Decouple deploy from release with the simplest tool that fits. Feature flags for user facing behavior. Config values for internal behavior. Dark routes for new endpoints. Every flag gets an owner and an expiry date. For databases, add the new column first, write to both old and new, backfill in batches, switch reads to the new column, then remove the old path in a later change. Avoid combining a schema change and a behavior change in one release.
Make the deploy path the only path. No building on a laptop, no copying files to a server, no routine manual SQL in production. The artifact that passed tests is the artifact that ships. The release captain can stop a release, and the team agrees in advance what a stop means. A hotfix still goes through the pipeline with a shorter review, not around it. Then remove approval gates that exist only because releases used to be scary.
Risks and how to manage them
Small changes can hide coupling. Two small changes deployed close together can interact in ways neither author expected. Watch the release, not just the deploy. Keep a change log with links to pull requests and flags. When an error rate moves, you can map it to a specific change instead of guessing.
Feature flags create debt. A flag that stays on forever is a branch in production. Keep a flag inventory with owner, creation date and removal date. Review stale flags every sprint. Remove the flag and the old code path as soon as the release is stable. Otherwise your codebase fills with hidden switches that nobody understands.
Speed can pressure people to skip security review, threat modeling or data checks. Don't let small mean unexamined. Define which changes need a security review by data touched and exposure, not by lines of code. A small change to an auth check or a payment path still gets the same review as a large one. The release captain can pause a release for that review without treating it as a failure.
There is also a people risk. A team measured on deploy count will deploy empty changes. A team punished for every rollback will stop rolling back. Measure the system, not the person. Make it safe to say this change is not ready and safe to say this flag is not working. The release queue should show the truth, even when the truth is messy.
How to measure success
Track the number of changes released, typical pull request size in files and lines, time from merge to release, and changes waiting in the queue. Add rollback count and flag age. Don't set targets that reward volume alone. A week with fewer releases and a smaller queue can be better than many tiny deploys nobody reviewed.
Look at the shape of the work. Are changes getting smaller? Is the blocked column shrinking? Are flags removed within a reasonable window? Is the same service always at the front of the queue? Those patterns show where the process is stuck. A rising deploy count with a rising rollback count means you are shipping faster than you can learn.
Ask the team one question each week: what stopped you from shipping this week? The answers are concrete: a flaky test, a missing staging dependency, an approval that sits in a chat thread, a database migration nobody wants to own. Fix those. If your team lacks the release plumbing, Cloudverg's Delivery platform setup at $14,500/month is one option among others. It covers pipelines, environments, secrets, monitoring and rollback in a four week scope. It won't split work for you. That part is a management habit.
Frequently asked questions
How small should a change be before it goes to production?
Small enough that one reviewer can understand it in one sitting and one person can describe it in two sentences. A single behavior change is a good rule. Refactors, dependency updates and schema changes usually belong in separate changes. If the rollback plan needs a meeting, split the work again.
Should every merge go straight to production?
Not necessarily. Every merge should be deployable, even if you release it later. Teams with strong tests and flags can deploy on merge, while teams with customer facing risk can deploy hidden and release on a schedule. The deploy habit matters more than the exact cadence.
What is the difference between deploy and release?
Deploy puts code on production infrastructure. Release makes a behavior visible to users. Feature flags, config values and dark routes let you deploy without releasing. That separation lets you ship small changes more often without turning every merge into a customer announcement.