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, 20264 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, the whole idea behind a CI/CD maturity model.

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

Say you run npm run build on a website. You get back a folder of files, ready to go live. That folder is called the artifact.

That artifact is the same folder no matter how it gets shipped. Whether one simple script uploads it, or ten careful checks review it first, the files inside don't change.

What changes across the three stages in this post is only the process around that folder, never the folder itself. So every time this post talks about a pipeline getting "more careful," picture the same artifact, just handled with more checks around it.

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:

🔑No long-lived credentials in a secret
👀Required reviewers on production deploys
📌Third-party actions pinned to a commit hash

A token that never expires is a token someone eventually finds. A human has to actually look before code reaches real users. And a version 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 stays constant. Only how carefully you ship it changes.
  2. You escalate stages because the cost of not escalating went up, never on a schedule.

Further Reading and Watching