Five Developers, One Release a Week
Friday afternoon. Someone merges the last ticket, someone else runs the deploy script, and five people stare at Slack for twenty minutes to see whether anything catches fire.
That isn’t a pipeline. It’s a ritual with a rollback plan made of hope.
Continuous integration means every push runs your tests automatically. Continuous deployment means code that passed those tests reaches production without anyone typing a command. A team of five shipping once a week needs both, and needs startlingly little beyond them.
That second half is where most CI/CD writing falls apart. The genre is produced by platform teams at companies that have platform teams, and it quietly assumes you can spare an engineer to run the thing.
You can’t. So this is the minimum viable deployment pipeline: the version you can finish in an afternoon, when nobody’s job title contains the word “platform”.
Weekly Puts You Mid-Table, and That’s Fine
DORA’s benchmarks have been steady for years. The 2024 report puts teams deploying between once a day and once a week in the high-performing band, and once a week to once a month in the medium one. Weekly is the line between them.
DORA’s 2025 edition turned to AI-assisted development and sorted its respondents into seven team profiles, so the bands above are the 2024 numbers, and they are still the ones a five-person team can plan against.
Here’s the useful reading of that. Weekly releases aren’t a failure state waiting to be fixed with more tooling, and the pipeline that carries you from weekly to daily is the same pipeline, run more often. Cadence is a confidence problem, not a tooling problem.
Which means the goal isn’t a sophisticated pipeline. It’s a pipeline boring enough that nobody hesitates to use it.
CI/CD Best Practices That Earn Their Keep at Five People
Six of them. Everything else waits until something specific goes wrong.
- Run the tests on the pull request, not after the merge. Catching a red build after it lands on main means one person fixes main while four wait. Branch protection that greys out the merge button on a failing check costs you a single checkbox.
- Keep main permanently deployable, and make deploying one action. If deploying involves a runbook, a sequence, or a person who knows the sequence, you have a bus factor of one and a deploy nobody wants to run on a Thursday.
- Deploy the artifact you tested. Build once, promote the same container or bundle through staging to production. Rebuilding at each stage means the thing in production was never actually tested, which is how “but it worked in staging” survives into its second decade.
- Keep secrets in the provider’s secret store and nowhere else. Not in the repo, not in a
.envsomeone shares over Slack, not in the deploy script. Rotating a leaked key is a bad afternoon; rotating one you can’t find every copy of is a bad week. - Own a rollback that a tired person can run at 22:00. One command, or one button, back to the previous release. If your answer is “revert the commit and redeploy”, time it once with the clock running and see whether you still like the answer.
- Keep the whole pipeline under ten minutes. Past that, people stop waiting for it and start batching changes, which puts you back where you started. Parallelise the test suite before you optimise anything else.
Notice what’s missing. No environments beyond staging, no approval workflow, no dashboard.
The Practices That Are Premature at This Size
DevOps discourse treats these as table stakes. For five people shipping weekly, each one is a cost with no matching benefit.
Kubernetes. It solves scheduling across many machines and many services. You have one service and one machine. A managed platform or a single Docker host does the same job with roughly none of the operational surface.
Canary and blue-green deploys. Both exist to limit blast radius at traffic volumes where a bad release reaches thousands of users before a human notices. At weekly cadence with a rollback you’ve actually rehearsed, you notice first.
GitOps controllers. ArgoCD and Flux are excellent at reconciling declared state across many clusters. With one cluster you’re maintaining a reconciliation loop to deploy an app you could deploy with a webhook.
A feature-flag platform. Flags themselves are useful and a boolean in your config is a flag. The subscription, the SDK, and the per-seat billing are for teams running experiments across multiple squads.
Multi-stage approval gates. Approval gates encode organisational distrust. In a team where everyone reviews everyone’s pull requests, adding a second gate after the review just adds latency.
A full end-to-end browser suite. Slow, flaky, and expensive to maintain. Cover the two or three flows that generate revenue, and let integration tests handle the rest until the suite’s maintenance cost is worth paying.
An on-call rotation. Five people can’t sustain one anyway. Alert to a shared channel, agree who looks first, revisit when you’re twelve.
Every item on that list is correct advice for somebody. Just not for you, not yet.
GitHub Actions vs GitLab CI: The Answer Is Boring and Still Right
Use whichever one already hosts your code.
That answer disappoints people, so here’s why it holds. Both products run the same lint, test, build, deploy stages against the same container images, both are free at small-team volume, and the concepts transfer directly between them. Migrating a working pipeline costs a week of somebody’s attention and produces a pipeline that does what the old one did.
The differences that do show up at five people are about shape, not capability.
GitHub Actions spreads configuration across multiple workflow files and leans hard on a marketplace of prebuilt actions. That marketplace is genuinely the biggest advantage on offer, and also the biggest supply-chain surface in your pipeline: pin third-party actions to a commit SHA rather than a moving tag.
GitLab CI puts everything in one .gitlab-ci.yml and bundles a container registry, environments, and review apps into the same product. Fewer moving parts, a smaller ecosystem, and a config file that gets genuinely unwieldy past a few hundred lines.
The one case for switching: your code is moving anyway. Otherwise the pipeline follows the repo, and that’s the whole decision.
What the Three Big Providers Actually Charge
Pricing model shape matters more than the headline number, because the shape determines which growth pattern surprises you.
| GitHub Actions | GitLab CI | CircleCI | |
|---|---|---|---|
| Billing shape | Minutes per account, priced by runner OS | Compute minutes per top-level namespace, the pool set by subscription tier | Credits, converted from compute time and resource class |
| Free tier | 2,000 minutes/month on the Free plan; public repos run free on standard runners | 400 compute minutes/month per namespace; private free namespaces capped at five users | 6,000 build minutes/month on the small Docker class, five active users |
| First paid step | Team plan, 3,000 minutes/month | Premium at $29 per user/month billed annually, 10,000 minutes | Performance from $15/month, five active users included; each extra user costs 25,000 credits, about $15 |
| Where it gets annoying | macOS minutes cost roughly ten times Linux ones, so one macOS leg in a build matrix drains the allowance | 400 minutes is a fortnight of real pipelines; the pool is per namespace, so hiring adds no minutes and more minutes are a separate purchase | Credits rather than minutes: Docker layer caching and network egress bill separately, so run cost is hard to predict |
| Escape hatch | Self-hosted runners carry no per-minute charge | The quota meters GitLab’s own instance runners, so a runner you host draws nothing | Self-hosted runners on every plan, free one included |
Figures above are as of 2026 and vendors revise them; check before you budget.
The macOS multiplier is the one that catches teams out. A Linux minute on GitHub’s standard two-core runner costs a fraction of a cent, a macOS minute costs about ten times that, and a mobile team running a build matrix across both discovers this in a billing email rather than a pull request.
GitLab’s shape has a different edge. The compute pool sits on the top-level namespace, so five people and fifty people start from the same allowance, and 400 minutes on the free tier disappears fast once more than one project is pushing. The five-user cap on private free namespaces is the harder wall: exceed it and the namespace goes read-only.
CircleCI’s credit model is the most flexible and the least predictable. Compute converts to credits at a rate that depends on the resource class you chose, and add-ons bill on top, so “what did this pipeline run cost?” takes arithmetic rather than a glance.
None of that should drive the choice at five people. All of it should drive whether you notice before the invoice does.
The Thing That Breaks First Isn’t the Provider
It’s the test suite.
A typical case: a four-person team sets up a clean pipeline in an afternoon, and it works. Six months later, three tests fail intermittently for reasons nobody has chased down, so the team has learned to re-run the job. Two more months and re-running is reflex, which means the pipeline no longer blocks anything.
That’s a dead pipeline that still shows green. The tooling is fine. The signal is gone.
Fix it the unglamorous way: quarantine the flaky test the day it flakes twice, file the ticket, and make the quarantine list something a person actually reads. A suite of forty trustworthy tests beats a suite of four hundred that nobody believes.
Pipeline duration rots the same way. Ten minutes becomes fourteen, fourteen becomes twenty-five, and people start batching three days of work into one merge, which is exactly the release risk CI was supposed to remove. Put the pipeline duration on a wall or in a weekly channel post, and treat a creeping number as a bug.
If your repository layout is part of the problem, that’s worth its own look. Monorepo pipelines that run every test on every push are a known way to turn ten minutes into forty, which we worked through in monorepo vs. polyrepo for small teams.
A Week-One Setup You Can Actually Finish
Four steps, in this order:
- Get the test suite running on every pull request, and turn on branch protection so a red build blocks the merge. Stop here for a week if that’s all you get done. It’s the half that catches bugs.
- Add a deploy job triggered by a merge to main. Where it deploys to is a separate decision, and we compared the realistic non-Kubernetes options in Coolify, Kamal, or just push to Cloud Run.
- Move every secret into the provider’s secret store and delete the copies. Then rehearse a rollback while nothing is on fire, and write the command down where an on-call-less team can find it at night.
- Wire build failures to the channel people already read. An alert nobody sees is a log entry with ambition.
For the stage-by-stage mechanics, including a working GitHub Actions workflow file you can adapt, read CI/CD pipelines explained. And once the pipeline runs, DevOps for SMBs covers what else belongs in a small team’s stack: monitoring the four golden signals, automated dependency updates, and the managed-platform choices that keep it all under a sane monthly bill.
Ship weekly. Make the pipeline boring. Add the sophisticated parts on the day an incident proves you need them, and not one sprint earlier.
Deploys still involve someone watching Slack with crossed fingers? Let’s talk it through. We’ll look at what you ship, how often, and what’s actually worth automating first.

