İçeriğe geç
wedevit

August 5, 2026 · 9 min read · software

İlhan Buğra Aslan

Why does every release feel risky? CI/CD for small teams


Releases feel risky because they are rare and large, not because the team is careless. A team that ships once a month moves hundreds of changes in a single evening; when something breaks, finding which change caused it takes hours, and the rollback path has usually never been tried. The fix is not more caution, it is smaller batches and less manual work. Five things do most of it: measuring delivery speed and stability, a pipeline that gives feedback in ten minutes, one build artifact promoted to every environment, deployment separated from release, and a rollback step you rehearse on purpose. Once those are in place, release night stops existing, because releasing becomes an ordinary step in the day.

Start with the numbers, because you cannot argue about what you do not measure

DORA's software delivery metrics are the shared vocabulary here. Deployment frequency is how often you ship to production over a given period. Change lead time is how long it takes for a change to go from committed in version control to running in production. Change fail rate is the share of deployments that need immediate intervention. Failed deployment recovery time is how long it takes to get back to a good state after a bad deploy. In 2024 DORA added a fifth one, rework rate, which captures unplanned deployments made because of a production incident.

None of this requires buying a tool. The first three can be pulled by hand from git tags, deploy logs and your incident list. What matters is reading throughput and stability together. Speed alone is not good news: rising deployment frequency while stability falls just means you are breaking things faster. One ground rule to set up front, though, is that these never get reported per person. A team-level trend is useful; a number that feeds performance reviews turns into a game.

The real variable is batch size, not tooling

This is worth understanding before you automate anything. Risk scales with how much change rides in each deploy, not with how many deploys you make. DORA's guidance on working in small batches gives a usable threshold: any batch of code that takes longer than a week to complete and check is too big, and work should be split so it finishes in hours to a couple of days. The branching side has measurable rules too. DORA's trunk-based development page recommends keeping three or fewer active branches, merging to trunk at least once a day, and dropping code freezes and separate integration phases. It also notes that branches in this style typically live no more than a few hours.

The quiet enemy of small batches is heavyweight human approval. DORA's work on streamlining change approval reaches a conclusion that sounds backwards at first: they found no evidence that a formal, external review process was associated with lower change fail rates, while heavyweight approval approaches did show a negative effect on delivery performance. The mechanism shows up at the end of the chain. Waiting for approval delays the deploy, the delay grows the batch, and the bigger batch carries more risk. What DORA suggests instead is a swap: satisfy segregation of duties with peer review inside the team, let continuous testing and monitoring catch bad changes, and give any approval board a coordination and process improvement role. Their framing of the goal is one sentence long. Make your regular change process fast and reliable enough that you can use it for emergency changes too.

The ten-minute rule is from 2004 and still holds

In the second edition of Extreme Programming Explained, Kent Beck and Cynthia Andres define one of the primary practices this way: build the whole system and run all of the tests in ten minutes, because a build that takes longer will be used much less often and the feedback opportunity is lost. Twenty years on, ten minutes is still a good target, and the reason is behavioural rather than technical. A 40-minute pipeline pushes a developer to save up work, and saved-up work is a big batch again.

The practical split looks like this. The commit stage runs the build, unit tests, type checks and the linter, and it stays under ten minutes. End-to-end browser tests, load tests and migration rehearsals move to a later stage as separate jobs running in parallel. Setting up a dependency cache and parallelising the test suite is usually about a day of work, and it pays back on every single commit.

Build once, promote the same artifact everywhere

A pipeline that rebuilds per environment ships something similar to what you tested, not the thing you tested. The right pattern is a single artifact: the image or package is built once, gets a version tag, and the same output is promoted to staging and then to production. Environment differences live in configuration rather than code, so in connection strings, keys and feature flags. Behind most instances of "it worked on staging" sits a second build that resolved a different dependency version. Pin dependencies with a lock file, and reference images by digest instead of a floating tag.

Deploying and releasing are two different things

Separating these two is one of the highest-leverage moves available to a small team. DORA's small-batches guidance endorses feature toggles directly: configuration settings can switch the behaviour of new code, make UI elements visible or invisible, and enable or disable service logic. That lets an unfinished feature sit in production without being exposed to users, which is what dark launching means. The payoff is batch size again. Work in progress does not wait in a branch for three weeks, it waits in production behind an off switch.

Flags have a cost, so plan for it. Each one is a branch in the code and a doubling of what needs testing. Give every flag an owner and an expiry date, and delete the dead path within two weeks of turning it fully on. The one kind that earns a long life is the kill switch that disables a risky integration or an expensive feature.

The database is the most dangerous step in the pipeline

Code can be rolled back; a dropped column cannot. On top of that, a rolling deploy runs the old and new versions side by side for a while, so the schema has to work with both. The usual answer is the expand/contract pattern. First you expand the schema additively, so the new column appears while the old one stays. Then you migrate the data and move the code to the new shape. Only at the end, once you are sure nothing reads it, do you drop the old structure. One breaking change becomes three separate deploys.

Two rules keep this honest. A migration must run cleanly underneath the version of the app that is still live. And renaming is not an operation you get to do; add, copy, drop is. On large tables, measure the lock duration in advance, because a migration that was never rehearsed against production-sized data is the most expensive step you own.

Rollback is a drill, not a document

"We'll roll back if there's a problem" is a wish rather than a plan until somebody has done it. The measurable version is DORA's failed deployment recovery time, and the only way to know that number is to try. The checklist is short: the previous artifact must still be deployable, an automatic health check must run after each deploy, the pipeline must return to the previous version by itself when that check fails, and at least two people on the team must be able to trigger it alone. Roll back deliberately once a month, during working hours. A rollback attempted for the first time at 11pm on a Friday is a second incident, not a recovery.

Your pipeline holds more privilege than anything else you run

The CI runner effectively holds your production keys, your cloud identity and your package registry token. March 2025 showed the price of forgetting that. Tags on the widely used tj-actions/changed-files GitHub Action were repointed to a malicious commit, and secrets were dumped into workflow logs. CISA published an advisory on it. The scale is instructive: more than 23,000 repositories used the action, and according to Endor Labs' analysis 218 repositories actually leaked secrets, most of them GitHub tokens that expire when the workflow ends. Damage stayed limited, but the technique worked.

GitHub's hardening guide offers three concrete steps. Pin third-party actions to a full-length commit SHA, which the docs describe as currently the only way to use an action as an immutable release. Set the default GITHUB_TOKEN permission to read access on repository contents only, then raise it per job where a job genuinely needs more. For cloud access, authenticate with OpenID Connect instead of storing long-lived credentials as secrets. Add one more of your own: never hand secrets to workflows that run on pull requests from forks. We cover the neighbouring ground in secrets management and software supply chain security.

AI is a test of your pipeline

The 2024 DORA report found that a 25% increase in AI adoption was associated with roughly a 1.5% drop in delivery throughput and a 7.2% drop in delivery stability. The reason has less to do with the quality of generated code than with its volume: as production speeds up, batch size grows, and batch size is where the risk lives. The 2025 report frames it in a way that completes the picture, calling AI an amplifier that magnifies existing strengths and weaknesses. If your pipeline gives feedback in ten minutes and your rollback works, the extra code turns into value quickly. If your pipeline is a manual release evening, the same tool breaks things faster.

What fits into this week

Try answering five questions in one sentence each. One: how many times did you ship to production in the last 30 days? Two: what is the median time from a change being merged to it running in production? Three: how many manual steps does a release take? Four: when was the last rollback attempted, and how long did it take? Five: which secrets can the pipeline reach, and which third-party actions are still unpinned?

The fastest win is usually hiding in question three. Collapsing manual deploy steps into a single script takes a few days for most teams, and it lowers lead time and late-night mistakes at the same time. For why the pipeline should come first when you modernise an old system, see renewing legacy software; for how the same discipline plays out at integration boundaries, see data synchronisation.


Need help with this topic?

get in touchall posts