The Three Stages of CI/CD Maturity

Why a good pipeline doesn't jump straight to enterprise-grade, and the question that actually decides when to add the next layer of safety.

September 1, 20265 min read

I used to think a "good" CI/CD pipeline was just the most secure one you could build. Branch protection, signed commits, locked-down permissions, all of it, from day one.

Then I watched a proof-of-concept project die waiting for its pipeline to be perfect.

A pipeline that's too careful too early is just as broken as one that's not careful enough. The trick isn't picking the safest setup. It's knowing which stage you're actually in.

That's the whole idea behind a CI/CD maturity model: match the pipeline's rigor to what the project has actually earned, not to what looks impressive in a diagram.

Developers burned 11.5 billion GitHub Actions minutes running tests last year alone, up 35% from the year before, according to GitHub's own 2025 Octoverse report. At that scale, getting the fundamentals right is worth the hour it takes.

The one thing that never changes

Two things get lumped together that shouldn't be: what you're shipping, and how carefully you ship it.

Call the first one the artifact. A built frontend, a compiled binary, a Docker image. It's the same file whether your pipeline is one YAML step or a twelve-stage security-reviewed monster.

The pipeline around the artifact is what changes. This post is only ever about that pipeline, never the thing it's carrying.

The three stages of CI/CD maturity, with the artifact constant at every stage ExpandThe three stages of CI/CD maturity, with the artifact constant at every stage

Stage one: get it out the door

The first stage only has one job: prove the thing works end to end, from a push to a live URL.

At this point, doing things "wrong" on purpose is fine. Hardcode a bucket name. Skip the tests. Deploy on every push to every branch if that's what gets you a working link fastest.

Speed beats correctness here, because nothing is correct yet. Polishing the pipeline before you know the idea works is solving a problem you might not have next week.

The failure mode isn't sloppiness. It's staying here too long, or over-building before the project has earned it.

Stage two: make it safe to collaborate

The pipeline earns a second stage the moment more than one person needs to touch the code without stepping on each other.

A direct push to main stops being allowed. Pull requests get required checks: build, lint, tests. Nobody merges past a red check.

Three things usually show up together at this stage:

  • Job separation. Build and deploy become two distinct jobs, not one long script, so a flaky deploy step doesn't force you to rebuild.
  • Caching. Installing the same dependencies on every single run wastes minutes you'll feel the moment your team grows past one person.
  • Reusable logic. Copy-pasted YAML across two workflow files is the same bug waiting to happen twice.

None of this is about security yet. It's about making the pipeline something a second person can read, trust, and safely change.

Stage three: make it safe to operate

This is the stage people jump to too early: making the pipeline safe even if someone tries to attack it.

The question changes here. Stage two asked "can a teammate break this by accident?" Stage three asks "what happens if someone tries to break this on purpose?"

A few things only make sense once you're actually at this level:

  1. No long-lived cloud credentials sitting in a secret. A token that never expires is a token someone eventually finds.
  2. Required reviewers on production deploys. A human has to actually look before code reaches real users.
  3. Third-party actions pinned to a commit hash, not a version tag. A tag can be moved to point at different code. A commit hash can't.

I already ran into the credentials problem building a passwordless frontend on S3 and CloudFront: the same instinct that says "don't let the frontend hold a password" applies to your pipeline too. Don't let your pipeline hold a credential it can lose.

The question that decides when to move up a maturity stage

Here's the part that took me longest to get right: you don't move stages on a schedule. You move because the cost of not moving just went up.

  • Nobody else needs to touch the code yet? Stage one is still correct, not lazy.
  • A second contributor joined and pushed straight to main by mistake? That's your signal for stage two.
  • The pipeline now touches production data or real users? That's your signal for stage three, whether or not you feel "ready."

I've seen the opposite mistake just as often: a two-person side project with an enterprise-grade pipeline nobody can modify without reading three docs first. That's not safety, that's a pipeline reviewed for a threat model the project doesn't actually have.

A pipeline you're constantly fighting with isn't a well-secured pipeline. It's a slow one wearing security as an excuse.

The Essentials

  1. The artifact and the pipeline are two different things. The artifact stays constant. Only how carefully you ship it should change across stages.
  2. Stage one optimizes for proof, not correctness. Hardcoded values and skipped tests are fine when you're still validating the idea itself.
  3. Stage two exists the moment a second person touches the code. Job separation, caching, and reusable workflows make the pipeline something a teammate can trust.
  4. Stage three's real question is adversarial. Not "can a teammate break this by accident" but "what happens if someone tries to break this on purpose."
  5. You escalate stages because the cost of not escalating went up, not on a fixed schedule. A pipeline too advanced for its own project is its own kind of mistake.

Further Reading and Watching