← Back to libraryQuestion 218 of 273
🔧CI/CDBeginner

Anatomy of a Pipeline (Stages, Jobs, Steps, Runners)

📌 Definition:

A pipeline is an automated sequence that takes a code change from commit to (potentially) production. It is composed of stages (logical phases like build, test, deploy), which contain jobs (units of work that can run in parallel), which contain steps (individual commands), executed on runners (the machines or containers that do the work).

📖 Detailed Explanation:

Understanding this hierarchy is foundational because every CI system (GitHub Actions, GitLab CI, Jenkins) uses the same shape with different names. A stage groups related work and usually gates the next: tests only run after the build succeeds. Jobs within a stage typically run in parallel on separate runners, which is how you fan out browser or shard combinations. Steps run sequentially within a job and share its filesystem and workspace. Runners are ephemeral by default — each job starts on a clean machine/container, so anything one job needs from another (a built artifact, a coverage file) must be explicitly passed via artifacts or caches, not assumed to persist. This ephemerality is the single most common source of confusion for people new to CI.

🔑 Key Points:
  • Hierarchy: pipeline → stages → jobs → steps, run on runners
  • Stages gate progression (test after build); jobs in a stage run in parallel
  • Steps run sequentially and share the job's workspace/filesystem
  • Runners are ephemeral — each job gets a clean machine by default
  • Cross-job data (artifacts, caches) must be passed explicitly, never assumed
🌍 Real-World Example:

A pipeline has a build stage (one job compiling the app), a test stage (three parallel jobs: unit, API, E2E, each on its own runner), and a deploy stage. The E2E job can't see the build job's output automatically — the build job uploads the compiled bundle as an artifact, and the E2E job downloads it, because each runs on a fresh, isolated runner.

📎 Code Example:
# GitHub Actions — stages expressed via job dependencies (needs:)
jobs:
  build:                     # stage 1
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run build
      - uses: actions/upload-artifact@v4
        with: { name: dist, path: build/ }

  test:                      # stage 2 — runs only after build
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/download-artifact@v4
        with: { name: dist, path: build/ }
      - run: npm ci && npm test
🎯 Scenario-Based Interview Question:

Scenario: A teammate's E2E job fails with 'build/ not found', even though an earlier build job clearly produced it and 'it works locally'. They insist the CI is broken. What's actually happening and how do you fix it?