A delivery pipeline has one job: to give an engineer a trustworthy verdict on a change as quickly as possible, and then to carry that exact change to production without altering it. Almost every pipeline design principle follows from those two clauses.

Build once, promote everywhere

The single most important structural rule: an artifact is built exactly once and then promoted, unchanged, through every environment.

flowchart LR
    subgraph "Correct"
    A1[Commit] --> B1[Build once] --> C1[(Artifact<br/>sha256:ab12…)]
    C1 --> D1[Deploy to test]
    C1 --> E1[Deploy to staging]
    C1 --> F1[Deploy to prod]
    end
flowchart LR
    subgraph "Broken"
    A2[Commit] --> B2[Build for test] --> D2[Test env]
    A2 --> B3[Build for staging] --> E2[Staging env]
    A2 --> B4[Build for prod] --> F2[Prod env]
    end

If you rebuild per environment, you have tested one artifact and shipped a different one. Dependency resolution, base image tags, and build-time toolchains all drift between runs. The version that passed staging is not the version in production, and the difference is invisible.

Configuration therefore cannot be baked into the artifact. It is injected at deploy time from the environment, which is a much stricter discipline than it sounds — it rules out per-environment build flags, environment-specific compilation, and config files committed inside the image.

Order stages by feedback economics

Stages should be ordered by the ratio of information gained to time spent. Cheap, high-signal checks run first; expensive, narrow checks run last.

StageTypical durationBlocks merge?Rationale
Lint, format, type checksecondsYesCatches the largest class of trivial defects instantly
Unit tests1–3 minYesHigh coverage per second of runtime
Build + package2–5 minYesProduces the artifact everything downstream uses
Static security analysis2–5 minYes, for high severityCheap relative to a vulnerability reaching production
Integration tests5–15 minYesReal signal, but slow and more brittle
Deploy to staging2–5 minYesValidates the deployment mechanism itself
End-to-end / smoke tests5–20 minYes for smoke, no for full suiteHighest fidelity, highest flake rate
Performance / load tests20+ minNo — run on a scheduleToo slow to gate every commit

The pragmatic target most teams aim for is under ten minutes from push to merge verdict. Beyond that, engineers context-switch, and a context switch costs more than the pipeline saved.

Fail fast, but fail informatively

A pipeline that stops on first error gives the fastest verdict. A pipeline that runs independent checks in parallel and reports all failures at once gives the most useful verdict. The right answer differs by stage:

  • Sequential dependencies (build must precede deploy) — fail fast.
  • Independent checks (lint, unit tests, security scan) — run in parallel and report all results, so the engineer fixes everything in one pass rather than discovering the next failure after each fix.

Failure output should name the fix, not just the symptom. “Lint failed” costs the engineer a log dive; “src/api/handler.go:42 — unused import fmt; run make fmt” costs them nothing.

The pipeline is a product with an SLO

Pipelines degrade quietly. The only defence is to measure them like production services.

Pipeline metricTarget to aim atWhat it protects
P50 / P95 durationP95 under 15 min for the merge-blocking pathEngineer flow state
Flake rate (pass on retry, no code change)Under 1% of runsTrust in red builds
Queue wait timeUnder 1 minEffective capacity
Success rate of main buildsAbove 95%Deployability of trunk
Time to restore a broken pipelineUnder 1 hourEveryone’s throughput

A broken shared pipeline blocks every engineer simultaneously, which makes it one of the highest-leverage incidents an organization can have. It deserves the same on-call treatment as a customer-facing service.

Structure: templates over copies

Once more than a handful of services exist, per-repo pipeline definitions diverge into unmaintainable variants. The pattern that survives is a small set of centrally maintained, versioned templates that individual repos reference with a pinned version.

flowchart TD
    T[Central pipeline templates repo<br/>versioned, tested, changelogged]
    T -->|"uses: org/templates/go-service@v3"| S1[Service A]
    T -->|"uses: org/templates/go-service@v3"| S2[Service B]
    T -->|"uses: org/templates/node-service@v2"| S3[Service C]
    S1 -.->|"repo-level overrides:<br/>extra test step"| O1[Local extension point]
    subgraph "Rules"
    R1[Templates are versioned and pinned]
    R2[Breaking changes ship a new major version]
    R3[Extension points are explicit, not forks]
    end

Two rules keep this from becoming a new bottleneck: templates must offer explicit extension points, so teams with genuine differences do not fork; and version pinning must be real, so a template change cannot break every pipeline in the organization simultaneously.

Secrets and permissions

Pipelines are an attractive attack surface: they hold credentials to every environment and execute code from every contributor.

  • Use workload identity / OIDC federation rather than long-lived cloud keys stored as CI secrets.
  • Scope credentials per stage. The unit-test stage needs no deploy permission.
  • Never expose deployment secrets to workflows triggered by untrusted forks.
  • Treat pipeline definitions as production code: protected branches, mandatory review, and an audit trail of changes.

Environment promotion

A promotion path should be explicit and one-directional:

flowchart LR
    A[(Artifact)] --> B[Dev<br/>auto on merge]
    B --> C[Staging<br/>auto after smoke tests]
    C --> D{Gate}
    D -->|automated checks +<br/>optional approval| E[Production<br/>progressive rollout]
    E --> F[Post-deploy verification]
    F -->|failure| G[Automatic rollback]

Where an approval gate exists, it should be a decision about business timing, not a re-check of things the pipeline already verified. Approval gates that require a human to review test results are a sign the tests are not trusted, and the fix is in the tests.

Adoption checklist

  • Artifacts are built once and promoted by digest, never rebuilt per environment.
  • Configuration is injected at deploy time, not baked into the artifact.
  • Merge-blocking path completes in under ten minutes at P95.
  • Independent checks run in parallel and report all failures together.
  • Pipeline duration, flake rate, and success rate are dashboarded.
  • Pipeline definitions come from versioned, pinned central templates.
  • CI authenticates to cloud providers via OIDC, not stored long-lived keys.
  • Approval gates decide timing, not correctness.

Last updated 19 Aug 2026, 00:00 UTC. history