Skip to content

CI/CD

Core (a load-bearing practice on every real engagement, not an optional extra). · prereq 00 (Setup & AI-native ways of working), 03 (Containers · Podman) — the build step's typical artifact is a container image; this module automates the "build → test → deploy" mechanics you've been doing by hand. · ~1 day (4 reference submodules — triggers & events, jobs/steps/runners, secrets & environments, and the build→test→deploy pipeline shape). Reality check: this is the 80/20 on-ramp; real fluency (multi-stage release pipelines, matrix builds across many targets, reusable workflows) is built on the job, one pipeline at a time.

Every module before this one ended at "commit it" or "push it" — a human still had to run the build, run the tests, and ship the result by hand. CI/CD is what removes the human from that loop: an event (a push, a pull request) triggers a workflow that runs the same build → test → deploy steps, the same way, every time, without anyone remembering to do it. GitHub Actions is GitHub's own CI/CD engine — the workflows live in the repo itself, versioned right alongside the code they build.

TL;DR — in 30 seconds:

  • A workflow is trigger → jobs → steps → runner: something happens, jobs run (parallel by default), each job's steps run sequentially on one disposable runner.
  • Jobs only wait on each other via needs: — that's the only thing turning "everything at once" into an ordered build → test → deploy pipeline.
  • A secret is masked wherever it would appear in a log; a plain workflow variable is not — never treat the two as interchangeable.

Learning objectives

By the end, the junior can:

  • Name a workflow's trigger (push, pull_request, schedule, workflow_dispatch) and explain why the trigger choice changes what the workflow is even allowed to assume about the code it's running.
  • Describe how a workflow decomposes into jobs (parallel by default, ordered only via needs:) and steps (always sequential, always on the same runner) — and where an action (uses:) differs from a raw shell command (run:).
  • Explain how secrets differ from plain workflow variables, where they're scoped (repository / organization / environment), and why they're masked in logs rather than just hidden.
  • Explain what an environment adds on top of a job (protection rules, required reviewers, environment- scoped secrets) and why "deploy to production" is usually gated differently than "deploy to staging."
  • Trace the classic build → test → deploy shape end to end: what each stage's job depends on, how an artifact moves between jobs that each run on a fresh, disposable runner.
  • Push a real workflow to a real repo and read its result from the Actions tab — not imagine what it did.

Do it with Claude

Pair, Do and Prove run in Claude Code, with the onboarding-tutor skill. Click a button to copy the exact prompt, then paste it into Claude Code in this repo.

1. Learn · acquire the concept

No external course — read this brief, then go deeper in the four reference pages below.

Mental model (20 minutes):

A workflow is nothing more than: something happens → run these jobs → each job is a list of steps → each step runs on a runner.

flowchart LR
    A["Trigger<br/>push · pull_request · schedule · workflow_dispatch"] --> B["Workflow<br/>the .yml file"]
    B --> C["Job(s)<br/>parallel by default"]
    C --> D["Steps<br/>sequential, same runner"]
    D --> E["Runner<br/>a fresh disposable VM"]
  • Trigger — the on: key decides when a workflow runs at all: on every push, on a pull_request (so untrusted changes can be checked before merge), on a schedule (cron syntax), or manually via workflow_dispatch. The trigger also decides what context the workflow can trust — a pull_request from a fork, for instance, shouldn't be handed the same secrets as a push to your own branch.
  • Jobs — a workflow is one or more jobs; by default every job in a workflow starts in parallel, on its own fresh runner. A job only waits for another when you say so with needs: — that's what turns "build, test, and deploy all fired at once" into "deploy only after test passes."
  • Steps — inside one job, steps always run sequentially, on the same runner, sharing its filesystem. A step is either uses: (run someone's pre-built action — a reusable unit like "check out my code") or run: (a raw shell command).
  • Secrets — a secret (API token, deploy key) is stored encrypted at the repository, organization, or environment level, referenced as ${{ secrets.NAME }}, and automatically masked wherever it would otherwise appear in a log — never assume a plain workflow variable gets the same treatment.
  • Build → test → deploy — the classic pipeline shape chains jobs with needs: so each stage only runs once the one before it succeeded, and passes data forward as an artifact (since each job's runner starts empty and throws away its filesystem when the job ends).

Go deeper: Triggers & events · Jobs, steps & runners · Secrets & environments · Build → test → deploy

Read next: GitHub Actions documentation (linked above) — optional, not required to pass this module's gate.

Check yourself — two jobs in the same workflow, no needs: between them. What order do they run in?

Parallel, each on its own fresh runner, starting at the same time — needs: is the only thing that turns "both at once" into "one waits for the other." Without it, there is no implicit ordering between jobs at all.

Check yourself — why can't the deploy job just read a file the build job wrote to disk?

Every job runs on its own fresh, disposable runner that throws away its filesystem when the job ends — a build job and a deploy job never share a disk. The only way data crosses that boundary is an artifact: uploaded by the job that produced it, downloaded by the job that needs it.

Done when: you can name, in order, the five pieces a workflow run passes through (trigger → workflow → jobs → steps → runner), before moving to Pair.

2. Pair · passive → active

Drive the onboarding-tutor:

  • "Give me a workflow trigger and quiz me on whether the jobs in that workflow can safely assume the code is trusted."
  • "Walk me through why two jobs in the same workflow run in parallel by default, and what needs: changes about that."
  • "Quiz me on the difference between a repository secret, an environment secret, and a plain workflow variable — where each is visible and what happens to it in a log."
  • "Explain why a build job's output has to be uploaded as an artifact instead of the deploy job just reading the build job's filesystem."
  • "Give me a build → test → deploy workflow and make me trace which job depends on which, and why deploy is the one job that should sit behind an environment's protection rules."

Common gotchas

  • Assuming jobs run in the order they're written. Fix: jobs run in parallel by default, regardless of file order — the only ordering mechanism is an explicit needs:. If "test" must finish before "deploy," needs: has to say so.
  • Passing data between jobs via the filesystem. Fix: each job starts on a fresh, disposable runner with no memory of a previous job's disk — upload the data as an artifact in the producing job and download it in the consuming job.
  • Treating a plain workflow variable like a secret. Fix: only values stored as a secret (repository/organization/environment scope) get masked in logs; a plain env: variable is printed in the clear if a step echoes it.
  • Giving a pull_request-triggered workflow the same trust as a push-triggered one. Fix: a pull_request can come from a fork with untrusted code — scope secrets and write access to the trigger that actually earned them, not to every workflow uniformly.

Done when: you can trace the full trigger → jobs/steps → secrets → build/test/deploy chain unprompted, for a workflow the tutor gives you.

3. Do · produce an artifact

Exercise — ship a real workflow:

  1. In a repo you control (a fork or a scratch repo — do not experiment on shared team infrastructure), add a workflow file under .github/workflows/.
  2. Give it a trigger (on: push, scoped to a branch you're using) and at least two jobs: one that runs a trivial build/test command (e.g. a script that just echoes a status and exits 0), and a second that needs: the first and only runs once it succeeds.
  3. Pass at least one piece of data from the first job to the second using an artifact (upload it in the first job, download it in the second) — this is the part that proves you understand why jobs can't just share a filesystem.
  4. Push the workflow and open your repo's Actions tab. Watch the run: confirm the two jobs' order, and read the log to see the artifact being uploaded and downloaded.
  5. Write a short paragraph mapping what you saw in the Actions tab back to the Learn beat's diagram (trigger / workflow / jobs / steps / runner).

Done when: your workflow runs green in the Actions tab, the second job visibly waited for the first, and your paragraph correctly labels which part of the run corresponds to which layer of the diagram.

4. Prove · understanding gate

Mandatory. Pass bar in the Socratic gate.

  1. Triggers: Why would you scope a workflow to run on pull_request rather than push, and what changes about what that workflow can safely be trusted with (secrets, write access)?
  2. Jobs & steps: What decides whether two jobs run in parallel or in sequence? Why do the steps inside one job never need that same mechanism?
  3. Secrets & environments: Where can a secret be scoped, and what does an environment's protection rules add that a plain repository secret doesn't have?
  4. Build → test → deploy: Why does data have to move between jobs as an artifact instead of just being left on disk? What would break if the deploy job ran without needs: pointing at the test job?
  5. Provenance: You likely had the tutor walk you through at least one part of your workflow (a needs: chain, or the artifact hand-off). Which specific claim did it make, and how did you verify it — against your own Actions tab run — rather than taking the tutor's explanation on trust?

Pass bar: you can defend each piece with the underlying mechanism (why a trigger matters for trust, why parallelism is the default, what an artifact actually solves) — not just name the keyword — and show how you checked a tutor-supplied claim against your own workflow run. "It just runs the jobs" without the how = back to Pair.

5. Retain · fight forgetting

This module has no generated Anki deck yet — recall by re-reading the four reference pages and re-running the Do exercise against a different repo or a slightly different job graph until the trigger → jobs/steps → secrets → build/test/deploy chain comes back to you unprompted. Done when you can sketch that full chain, cold, for any workflow someone hands you.

Key takeaways

  • A workflow run always passes through the same five stages: trigger → workflow → jobs → steps → runner — every gotcha in this module traces back to a misunderstanding of one of those five.
  • Jobs run in parallel by default; needs: is the only mechanism that turns "everything fires at once" into an ordered build → test → deploy pipeline.
  • Steps inside one job always run sequentially on the same runner — that's why steps never need needs: between them, only jobs do.
  • A secret is masked wherever it would appear in a log; a plain workflow variable is not — never assume the two get the same protection.
  • Each job starts on a fresh, disposable runner with no memory of another job's filesystem — data only crosses that boundary as an artifact, uploaded by the producing job and downloaded by the consumer.
  • A pull_request-triggered workflow can run untrusted code from a fork — scope secrets and write access to the trigger that actually earned them, not uniformly across every workflow.

Go deeper — curated resources

Hand-picked external material to take this topic further — the best books, courses, talks, and writing. Cost is tagged Free, Free online (full text/course free on the web), or Paid.

  • Book · Continuous Delivery — Jez Humble & David Farley. The book that defined CD and named the build → test → deploy discipline this module practices. Paid.
  • Video · Continuous Delivery — Dave Farley. Practical, hard-won CD and testing wisdom from a co-author of the book, in short weekly videos. Free.
  • Docs · GitHub Actions Documentation — GitHub. Workflows, jobs, runners and secrets straight from the source — the reference for everything in this module. Free.
  • Repo · Awesome Actions — Sarah Drasner (sdras). A curated index of GitHub Actions and community resources to pull from once you build your own workflows. Free.
  • Book · Accelerate — Nicole Forsgren, Jez Humble & Gene Kim. The research proving that CI/CD and deployment automation actually drive delivery performance. Paid.

Gate log (mentor fills on pass)

  • Date passed:
  • Passed by: / checked by:
  • Weak spots noticed (feeds system improvement):