Why this matters
Build and release pipelines turn code into reliable, shippable software. As a Backend Engineer, you will:
- Automate builds, tests, security checks, and packaging.
- Publish versioned artifacts or images to registries.
- Promote releases through staging to production with approvals.
- Roll back quickly when something goes wrong.
- Keep secrets safe and environments consistent.
Concept explained simply
A pipeline is a set of automated steps that take your source code from commit to running software in an environment.
- Build pipeline: compile, lint, test, package, and store artifacts.
- Release pipeline: deploy built artifacts to environments (dev → staging → prod) with checks and approvals.
Mental model
Think of a factory conveyor:
- Station 1: Verify (lint, unit tests)
- Station 2: Assemble (build/package Docker image)
- Station 3: Inspect (integration tests, security scan)
- Station 4: Ship (publish artifact/image)
- Station 5: Deliver (deploy to environments with gates)
Every station is repeatable, observable, and leaves a trace (logs, artifacts, tags).
Core principles
- Repeatability: Same inputs produce same outputs.
- Idempotence: Re-running a step does not produce unintended side effects.
- Immutability: Build once, deploy the same artifact everywhere.
- Separation: Build vs. release concerns are decoupled.
- Traceability: Every release is identifiable (commit, tag, version, changelog).
- Security: Least-privilege, secret scoping, signed artifacts where possible.
Worked examples
Example 1: Minimal build pipeline (Node.js)
Goal: On each push/PR to main, install deps, test, build, and upload an artifact.
name: ci
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- run: npm ci
- run: npm test -- --ci
- run: npm run build
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: dist
path: dist
- Cache improves speed while keeping determinism.
- Artifacts allow later jobs to reuse built outputs without rebuilding.
Example 2: Build and push Docker image to a registry
Goal: Produce a versioned container image and push to a registry (GHCR in this example).
name: docker-build-push
on:
push:
branches: [ main ]
jobs:
docker:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write
steps:
- uses: actions/checkout@v4
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=ref,event=branch
type=sha
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- Build once on main; downstream jobs deploy the same image tag to all envs.
- Use immutable tags (commit SHA) for reliable rollbacks.
Example 3: Release pipeline with staging and production promotion
Goal: On version tag (e.g., v1.2.3), deploy to staging automatically, then promote to production with approval.
name: release-deploy
on:
push:
tags:
- 'v*.*.*'
jobs:
deploy-staging:
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v4
- name: Fetch artifact or image tag
run: echo "IMAGE_TAG=${GITHUB_SHA}" >> $GITHUB_ENV
- name: Deploy to staging
run: |
echo "Deploying $IMAGE_TAG to staging..."
# kubectl apply -f k8s/overlays/staging
deploy-prod:
needs: deploy-staging
runs-on: ubuntu-latest
environment: production
steps:
- name: Manual approval gate occurs via environment protection
run: echo "Awaiting approval in environment: production"
- name: Deploy to production
run: |
echo "Deploying $GITHUB_SHA to production..."
# kubectl apply -f k8s/overlays/production
- Configure environment protections (reviewers) in the repository settings for the production environment.
- Trigger by annotated tags following SemVer (vMAJOR.MINOR.PATCH).
- Roll back by redeploying a previous tag/commit.
How to structure build vs. release
- Build once per commit to main; publish immutable artifacts/images.
- Release pipelines should only fetch and deploy those artifacts; no rebuilding.
- Use environment-specific config via variables, not different binaries.
- Gate production with approvals and automated checks (smoke tests, health probes).
Versioning and traceability
- Use SemVer and git tags (e.g., v1.4.2) to cut releases.
- Attach build metadata (commit SHA, build number) to artifacts and images.
- Record release notes and link to changes (commits/issues) in your platform.
Security and secrets
- Store secrets in your CI/CD platform’s secret store; never commit them.
- Scope secrets per environment with least privilege.
- Consider signing artifacts/images and verifying signatures during deploy.
Exercises
These mirror the exercises at the bottom of the page. Try them here first.
Exercise 1: Minimal CI build
- Create a pipeline that installs deps, runs tests, builds, and uploads an artifact on pushes and PRs to main.
- Make builds deterministic (lockfiles, pinned versions when possible).
- Outcome: Passing pipeline and a downloadable artifact.
Exercise 2: Tag-driven release with staging → prod
- On tag vX.Y.Z: deploy to staging automatically.
- Require approval to deploy to production.
- Outcome: A promoted release with clear version and logs.
Self-check checklist
- Build artifacts/images are created once and reused.
- Re-runs produce the same artifact digest or contents.
- Version tags map to deployed versions.
- Secrets are not printed in logs.
- Staging deploys automatically; production requires an explicit gate.
- Rollback is described and tested at least once.
Common mistakes and how to self-check
- Rebuilding on release: Release pipeline compiles again. Fix by publishing and reusing artifacts/images from build jobs.
- No versioning: Deploying latest without a tag. Fix by enforcing SemVer tags and recording image digests.
- Mixed env config: Hardcoded staging/prod values in code. Fix by using environment variables or config files per environment.
- Leaking secrets: Echoing secrets in logs. Fix by masking secrets and using dedicated actions for auth.
- No rollback plan: Unsure how to revert. Fix by documenting commands to redeploy a previous tag and testing the process.
- Unclear ownership: No approvers for prod. Fix by setting environment protections and on-call ownership.
Practical projects
- Project A: Monorepo with two services; build matrices, cache, and parallel tests; single release pipeline promoting both with coordinated version tags.
- Project B: Containerized service with SBOM generation and vulnerability scan; block production on critical findings; signed images.
- Project C: Blue/green deployment script; health-check after deploy; automatic rollback on failure signal.
Who this is for
- Backend Engineers who ship services regularly.
- Platform/DevOps engineers designing delivery workflows.
- Developers adopting trunk-based development and automation.
Prerequisites
- Basic git and branching.
- Comfort with command-line tools.
- Familiarity with your app's build/test steps; optional Docker knowledge for container builds.
Learning path
- Automate build and tests for pull requests.
- Produce and store artifacts/images deterministically.
- Introduce environment-specific configuration and secrets.
- Add staging deployment with smoke tests.
- Gate production with approvals and rollout strategy.
- Document rollback and practice it.
Mini challenge
Cut a v0.1.0 tag for your service. Deploy to staging, run a smoke test, then promote to production with an approval gate. Verify version and commit SHA in production logs.
Next steps
- Add automated release notes from commits.
- Introduce canary or blue/green deployments.
- Implement SLO-aligned checks that block promotion when error rates spike.
Quick test
This test is available to everyone. Only logged-in users have their progress saved.
When you finish, review explanations to reinforce learning.