Lesson 30 of 30
A real, flawed pipeline with four separate problems stacked on top of each other. Find each one, understand why it's a problem, and fix it — this is the whole track, applied at once.
20 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
Every lesson in this track has been one concept in isolation. A real pipeline you inherit at a job won't hand you its problems one at a time — they'll be stacked on top of each other, and the job is to notice all of them, not just the one that happens to be causing today's complaint.
Here's a pipeline that "works" — it's green most of the time — and has four real problems anyway. Read it the way you'd read one handed to you in an interview or on your first week at a new job.
name: CI
on:
pull_request:
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Install
run: npm install
- name: Debug env
run: env
- name: Unit tests
run: npm run test:unit
- name: Integration tests
run: npm run test:integration
- name: E2E tests
run: npm run test:e2e
- name: Build
run: npm run build
Problem 1 — no caching. npm install (not even npm ci) re-resolves and re-downloads the entire dependency tree from scratch on every single run. This is the single biggest lever on pipeline speed and it's just missing.
Problem 2 — secrets echoed in a debug step. run: env dumps every environment variable — including any deploy tokens or API keys injected as secrets — straight into the Actions log in plain text. Anyone with read access to this repo's Actions tab can read them. This is a leftover from someone debugging a config issue who never removed the line.
Problem 3 — no branch protection tying to this check. Nothing in this file causes a merge to be blocked if it fails — that's a repository setting, not a workflow setting, and it's easy to add a check and forget the second step. A red pipeline that doesn't stop a merge isn't a gate, it's a suggestion.
Problem 4 — tests running serially. Unit, integration, and E2E tests are three independent suites running one after another in the same job. If each takes 4 minutes, that's 12 minutes of wall-clock time for work that could overlap.
name: CI
on:
pull_request:
jobs:
install:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci # respects the lockfile, and setup-node now caches ~/.npm
unit-tests:
needs: install
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: npm }
- run: npm ci
- run: npm run test:unit
integration-tests:
needs: install
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: npm }
- run: npm ci
- run: npm run test:integration
e2e-tests:
needs: install
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: npm }
- run: npm ci
- run: npm run test:e2e
build:
needs: install
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: npm }
- run: npm ci
- run: npm run build
The debug-env step is simply deleted — it added nothing but risk. npm install becomes npm ci (install from the lockfile exactly, faster and deterministic) with cache: npm doing the caching. The three test suites and the build now run as four parallel jobs instead of four sequential steps, each depending only on install finishing, cutting wall-clock time roughly to the length of the slowest single suite instead of the sum of all four. And separately, in the repo's branch protection settings, unit-tests, integration-tests, e2e-tests, and build all get added as required status checks on main — the fix that makes a red pipeline actually block a merge, which no amount of YAML editing alone can do.
secrets.API_KEY reference at least gets GitHub's automatic log-masking. A key that ends up in a raw env dump, or an echo $TOKEN left over from debugging, bypasses that protection entirely — it was never recognized as sensitive in the first place.main — if branch protection was never wired to require those specific check names.The opening lesson of this track drew a line between "we do CI" as a claim and a pipeline that actually proves it — enforced automatically, on every push, blocking what it should block. Auditing a real, flawed pipeline like this one is that proof exercise in practice: caching, secrets discipline, required status checks, and parallel test execution aren't separate topics anymore here, they're the same pipeline, fixed.