We closed module 1 with a promise: Diego will stop building on his laptop; from then on, every change verifies itself, on a clean machine, before anybody merges it. This lesson is the step that comes before keeping that promise. And it is a step almost everybody skips, because it is tempting to open the editor straight away and start writing YAML. The problem is that a configuration file does not turn a team into a team that practises continuous integration, just as buying a pair of trainers does not turn anyone into a runner. CI is, first and foremost, a set of practices and agreements; the tool merely enforces them. In this lesson we will look at what those practices are, what the full life cycle of a change under CI looks like, what it means exactly for a build to be "green" or "red" and what discipline that demands, and what the conceptual anatomy of a workflow is. We will finish with the six rules the Reservalia team agrees on before writing a single line of configuration. In lesson 02-02 we will write that configuration.

Contents

  1. Why the rules come before the YAML
  2. The six practices that make up continuous integration
  3. The life cycle of a change under CI
  4. Green build, red build and the discipline of "stop the line"
  5. The conceptual anatomy of a workflow
  6. Reservalia's team agreement
  7. Common Mistakes and Tips
  8. Exercises
  9. Conclusion

  1. Why the rules come before the YAML

Imagine two teams. Both have an identical pipeline: it builds, runs the tests and posts a notice in a chat channel.

  • Team A: branches live for three weeks. When the build turns red, nobody looks at it until the next day. The notification channel is muted because "something is always failing".
  • Team B: branches live for half a day. When the build turns red, the person who broke it drops what they are doing. If it is not fixed in ten minutes, they revert their commit.

Team A has a build server. Team B practises continuous integration. The configuration file is the same; what differs is what the team does with the information it produces.

This distinction is the whole reason for this lesson. In 01-01 we saw the formal definition of CI; now we are going to pick apart the specific practices behind it, because it is those — and not the tool — that produce the benefits we measure with the DORA metrics from 01-05.

  1. The six practices that make up continuous integration

2.1. A single repository as the source of truth

Everything needed to build and test the system lives in one single versioned place: code, tests, build scripts, database migrations, pipeline configuration and infrastructure definition.

The practical test: if every laptop on the team vanished tomorrow, could I rebuild the system by cloning the repository? If the answer contains a "well, plus a script Diego has in his downloads folder", the repository is not the source of truth.

Reservalia half meets this. The code and the migrations are in github.com/reservalia/reservalia, but the deployment commands live in Diego's memory. We will settle that debt in module 3.

2.2. Frequent integration into the main branch

Every person integrates their work into main at least once a day. This is not a style tip: it is the operational definition of the word "continuous".

Remember the integration hell diagram from 01-01: the cost of merging grows non-linearly with the time branches live apart. Two one-day-old branches merge themselves; two three-week-old branches require an afternoon of archaeology.

An uncomfortable consequence: if a task takes five days, you cannot wait five days to integrate. You have to learn to break the work into pieces that can be integrated without breaking anything even while they are incomplete. We will look at the techniques for doing so (dormant code, feature flags) in 03-05; for now, just take away the idea that integrating often is a skill, not only a rule.

2.3. An automated build on every change

Every git push automatically triggers a process that builds the system and runs the tests on a clean machine. Without anybody pressing anything. With no exceptions.

The three important words are automatic (nobody decides whether it runs), every (not just before a release) and clean (a machine that starts from scratch, without the 47 tools Diego has had installed since 2021).

2.4. The main branch is always deployable

At any moment of the day, the latest commit on main must be a valid candidate for production. Not "almost ready": deployable.

This is the most demanding of the six practices and the one that most changes how a team works, because it outlaws the sentence "I will push it now and fix it this afternoon". And it is also the one that makes the whole of module 3 possible: if main is always healthy, deploying stops being an event and becomes a formality.

2.5. If the build breaks, the team stops ("stop the line")

This is the most frequently violated practice, and we develop it in section 4. In one sentence: a red build is the team's problem, not the problem of whoever broke it, and fixing it takes priority over any feature.

2.6. Fast feedback

A pipeline that takes 45 minutes does not give feedback: it gives a forensic report. By the time the answer arrives, whoever made the change is already on another task and has lost the context.

The practical benchmark, and one of the lines Diego already came out with in module 1 — "if CI takes longer than going for a coffee..." — is this:

Pipeline time What happens in practice
< 10 min The person waits for the result and fixes it there and then. Real feedback.
10–20 min They switch task; coming back costs a few minutes of re-contextualising.
20–40 min Several PRs pile up unverified; the failure arrives mixed in with other changes.
> 40 min The team starts ignoring the pipeline and skipping it "just this once".

Reservalia's target for this module's CI will be under 10 minutes on a typical pull request.

  1. The life cycle of a change under CI

Let us follow the complete journey of a specific change: Diego fixes a bug in the availability calculation that lets two appointments overlap when a business has a midday break.

flowchart TD
    A["Diego creates the branch<br/>fix/break-overlap"] --> B["Writes the test that<br/>reproduces the bug - it fails"]
    B --> C["Fixes the code<br/>until the test passes"]
    C --> D["Fast local check<br/>npm run lint && npm test"]
    D --> E["git push"]
    E --> F["Opens a Pull Request<br/>towards main"]
    F --> G{"The pipeline<br/>triggers itself"}
    G --> H["Clean machine:<br/>checkout + install deps"]
    H --> I["Quality · Tests · Build"]
    I --> J{"All green?"}
    J -- No --> K["Diego fixes it<br/>and pushes again"]
    K --> G
    J -- Yes --> L["Human review:<br/>design, not commas or tabs"]
    L --> M["Merge to main"]
    M --> N["The pipeline runs<br/>again on main"]
    N --> O["Artifact published<br/>reservalia/api:a3f9c21"]

There are four details in this flow worth underlining:

The local check exists, but it is short. Diego does not run the whole suite before pushing: he runs the fast things (lint and unit tests). The slow parts are the machine's job. A team that insists on running everything locally before every push is wasting the very thing CI is for.

The pipeline runs twice: once on the pull request and again on main after the merge. That is not useless redundancy. The PR is verified as your branch was; after the merge, main may contain commits that landed in the meantime. That window is precisely the problem the merge queue in lesson 02-07 solves.

Human review comes after the green. Nobody should review a PR whose pipeline is red: it is human time spent on something the machine already knows is wrong. And since the machine takes care of formatting, style and types (lesson 02-05), human review can talk about what matters: whether the design is right.

The merge produces an artifact identified by the SHA. Commit a3f9c21 produces the image reservalia/api:a3f9c21. That traceability is the subject of lesson 02-06.

  1. Green build, red build and the discipline of "stop the line"

4.1. What "green" means

A pipeline understands no nuance: every step ends with an exit code. Zero means success; any other value, failure. You can check it in your terminal:

npm test              # runs the tests
echo $?               # prints the exit code of the previous command
# 0  → all passed      → green step
# 1  → some failed     → red step, the pipeline stops there

"Green build" therefore means that every step returned 0. It does not mean "the code is good": it means that none of the automated checks we have decided to run has found a problem. The quality of your green is exactly the quality of your checks.

4.2. The assembly line metaphor

The expression stop the line comes from Toyota's production system: any worker can halt the assembly line on spotting a defect. Stopping an entire factory sounds outrageously expensive; it turns out to be far cheaper than letting the defect travel through every subsequent station.

In software it is identical. If main is red and the team keeps pushing changes on top of it:

  • The following commits are built on a defective base.
  • Each new red build may have a different cause, and now there are several mixed together.
  • Nobody knows whether their change works, because the failure was already there.
  • The signal is lost: red stops meaning anything.

That last point has a name: alarm blindness. When the build has been red for three days, red is no longer information, it is decoration.

4.3. The protocol when the build breaks

A protocol that works, written down explicitly:

  1. The person who pushed the change is responsible for the repair. Not out of blame: out of context. They are the one who knows what has just changed.
  2. It is announced in the team channel. "Main is red, I am on it." It stops three people debugging the same thing in parallel.
  3. Nobody merges anything in the meantime. Merging onto a broken base is adding variables to an equation that already does not add up.
  4. The 10-minute rule: if it is not fixed within ten minutes, the guilty commit is reverted. Reverting is not a personal failure; it is the fastest route back to a known state. The calm fix happens afterwards, in a new branch.
  5. If the failure comes from a flaky test, it is not simply re-run and forgotten: it gets written down. We will look at the quarantine policy for flaky tests in 02-04.

The "re-run and see" trap. Re-launching the build until it comes out green is the most effective way of destroying trust in the pipeline. If a test passes only sometimes, you have a problem now, even if the red disappears.

  1. The conceptual anatomy of a workflow

Before writing real configuration in 02-02, it is worth having the mental model. Every tool on the market — GitHub Actions, GitLab CI, Jenkins, CircleCI — shares the same four pieces, even if they call them different things.

flowchart LR
    E["EVENT<br/>push, pull_request,<br/>tag, cron, manual"] --> W["WORKFLOW<br/>pipeline"]
    W --> J1["JOB quality"]
    W --> J2["JOB test"]
    W --> J3["JOB build"]
    J1 --> S["STEPS<br/>sequential steps<br/>inside the job"]
    J2 --> R["RUNNER<br/>clean machine<br/>that runs the job"]
Piece What it is Important detail
Event The fact that triggers the run It is not launched by a person: it is launched by something that has happened in the repository
Workflow The file describing what to do in response to that event It lives inside the repository, versioned alongside the code
Job An independent unit of work Each job runs on its own machine; by default, in parallel with the others
Step A step inside a job They run in order; if one fails, the following ones do not run
Runner The machine (usually a container or a VM) where a job runs Ephemeral: it is born clean and dies when it finishes

Three consequences of this model that surprise everybody the first time:

  1. Jobs do not share a disk. If the build job generates dist/ and the publish job needs it, you have to pass it explicitly (workflow artifacts, lesson 02-06). Steps within the same job do share a disk with each other.
  2. The runner is ephemeral. Everything you install disappears when it finishes. That is why dependency caching is not a luxury, it is what stops you reinstalling the world on every run (lesson 02-03).
  3. The workflow is versioned with the code. Changing the pipeline is a commit, it is reviewed in a PR and it can be reverted. This is pipeline as code, and we will go deeper in 04-05.

In pseudocode, the structure we will write in the next lesson reads like this:

WHEN (pull_request towards main) happens
  RUN the job "test"
    ON a clean Ubuntu machine
      STEP 1: download the repository code
      STEP 2: install Node 20.11.0
      STEP 3: npm ci
      STEP 4: npm test

That is all. Everything we see in the rest of the module is variations and refinements on those four lines.

  1. Reservalia's team agreement

Marta gets Diego and Nuria together for an hour. They open no editor. They come out with six rules written into the repository's README.md:

# Rule Why
1 No branch lives longer than 2 days. If the work is bigger, it gets split. Avoids integration hell and keeps PRs small
2 Nobody pushes directly to main. Everything comes in through a pull request. main only receives verified code
3 A PR is not merged with a red pipeline, no exceptions and no "but it is urgent". A single exception turns the rule into a suggestion
4 Red main = the team's top priority. After 10 minutes with no fix, it gets reverted. The signal is only worth something if it is respected
5 A PR's pipeline must take less than 10 minutes. If it goes above that, it is treated as a defect. Slow feedback ends up being ignored
6 Every flaky test is written down and quarantined the same day; it is never simply re-run. An unreliable green is worse than a red

Note that none of the six mentions GitHub Actions, YAML or any product. They are behavioural rules; the tool will only serve to make them easy to follow and hard to skip. In lesson 02-07 we will translate rules 2 and 3 into real technical configuration (branch protection rules) so that they stop depending on goodwill.

And a realistic expectation: CI is not going to improve Reservalia's DORA metrics overnight. The first thing it will do is reveal problems that already existed but were invisible: tests that have not passed for months, dependencies that only work on Diego's laptop, ignored type warnings. The first two weeks of CI are usually uncomfortable. That is the sign it is working.

Common Mistakes and Tips

Mistake 1: believing that installing the tool is adopting CI. It is the central mistake of this lesson. A pipeline with three-week branches is an expensive build server. Before configuring anything, agree the rules.

Mistake 2: normalising the red build. The exact moment a team loses CI is the first time somebody says "yes, it has been broken since Tuesday, but it is a known failure". From then on, red tells you nothing.

Mistake 3: pipelines that grow unchecked. Every week somebody adds a step "just in case" and nobody removes any. Six months later, the pipeline takes 40 minutes and the team dodges it. Treat pipeline time as a budget: if you want to add three minutes, find somewhere to take them from.

Mistake 4: confusing "the PR is green" with "the code is correct". Green means none of your checks has found anything. If you have no test for the midday break case, the pipeline will stay green with the bug inside.

Tip 1: start with a minimal pipeline and grow it. A workflow that only runs npm ci && npm test and is green from day one is worth more than a perfect one that never manages to go green.

Tip 2: make the status visible. A status badge in the README.md and a notice in the team channel when main turns red cost five minutes and multiply the chance that somebody reacts.

Tip 3: measure pipeline time from day one. It is the metric that degrades most quietly and the one that most determines whether the team trusts CI.

Exercises

Exercise 1

A team claims to practise continuous integration. These are the observable facts from their last week:

  • They have a workflow that runs the tests on every push.
  • Feature branches live for between 8 and 15 days.
  • The main branch has been red for 4 days because of a test "that is known to fail".
  • The pipeline takes 35 minutes.
  • Merges to main happen on Thursdays, in a joint session.

State, for each of the six practices in section 2, whether the team meets it, and order the ones they do not by the order in which you would fix them.

Exercise 2

At 17:40 Diego pushes a change to apps/api that turns main red: an availability integration test fails. At 17:55 he still has not found the cause and has to leave. Marta needs to merge an urgent fix for a customer before 18:30.

Describe exactly what must happen, in order, according to the agreement in section 6. Justify the key decision.

Exercise 3

Using the pseudocode from section 5 (WHEN / RUN / ON / STEP), write a conceptual workflow that, when a PR is merged into main, runs two independent jobs: one that runs the lint and another that runs the tests. Then answer: could the second job reuse the files installed by the first? Why?

Solutions

Solution 1.

Practice Met? Reason
Single repository Probably yes Nothing suggests otherwise
Frequent integration No Branches of 8-15 days and weekly integration: the opposite of "at least daily"
Automated build on every change Yes The workflow triggers on every push
main always deployable No It has been red for 4 days
Stop the line No The failure has been normalised
Fast feedback No 35 minutes, far above the useful threshold

Order of repair: (1) get main green today — by fixing or reverting the failing test — because without a reliable signal no other improvement is measurable; (2) adopt stop the line, so it does not happen again; (3) reduce pipeline time, because while it takes 35 minutes the team will tend to integrate rarely; (4) shorten the branches and integrate daily, which is the deepest cultural change and the one that needs the previous three to lean on. Merging daily with a slow, unreliable pipeline is a guaranteed disaster.

Solution 2. The correct order: (1) Diego announces in the channel that main is red and that he is leaving; (2) the 10-minute rule applies, and it has already been exceeded: Diego's commit is reverted, which brings main back to green in a couple of minutes; (3) Marta opens her urgent fix as a PR against the now-healthy main, waits for the green and merges; (4) the next day, Diego redoes his change in a new branch with the test fixed.

The key decision is reverting rather than "merging the urgent fix on top". Merging onto a red main means Marta's PR pipeline will come out red too: nobody will be able to tell whether her fix works, and the customer would receive an unverified change. Reverting passes no judgement on Diego's work: it simply returns the system to a known state.

Solution 3.

WHEN (push to main) happens
  RUN the job "quality"
    ON a clean Ubuntu machine
      STEP 1: download the code
      STEP 2: install Node 20.11.0
      STEP 3: npm ci
      STEP 4: npm run lint
  RUN the job "test"
    ON another clean Ubuntu machine
      STEP 1: download the code
      STEP 2: install Node 20.11.0
      STEP 3: npm ci
      STEP 4: npm test

No, the second job cannot reuse the first one's files: each job runs on its own ephemeral runner, with its own disk, and both may run at the same time. That is why both repeat checkout and npm ci. What does avoid reinstalling everything from the internet is dependency caching, which we will see in 02-03; and what allows files to be passed from one job to another is workflow artifacts, the subject of 02-06.

Conclusion

This lesson has laid the foundations on which the rest of the module is built:

  • Continuous integration rests on six practices: a single repository as the source of truth, at least daily integration into the main branch, an automated build on every change, a main branch that is always deployable, stop the line when something breaks, and feedback in under ten minutes. None of the six mentions a tool.
  • The life cycle of a change goes from the short branch to the SHA-identified artifact, by way of a pipeline that runs twice — on the PR and on main — and a human review that arrives after the green.
  • Green and red are exit codes, not opinions. A green is worth exactly what your checks are worth, and a red that gets normalised stops being information.
  • The anatomy of any workflow is four pieces: event, job, steps and runner. Jobs are independent and do not share a disk; the runner is ephemeral; the workflow lives versioned inside the repository.
  • Reservalia already has its six rules written down before touching any YAML, and an honest expectation: the first weeks of CI are going to bring to light problems that have been hidden for months.

Now it really is time to write configuration. In the next lesson, Setting Up a CI Environment, we create Reservalia's .github/workflows/ci.yml file from scratch and explain it line by line: the event that triggers it, the runner it runs on, how the code is downloaded, how Node 20.11.0 is pinned by reading the .nvmrc, how a PostgreSQL 16.3 is brought up for the tests, and what to do when the workflow fails and the logs say nothing obvious. By the end of it, every Reservalia pull request will verify itself.

CI/CD Course: Continuous Integration and Deployment

Module 1: Introduction to CI/CD

Module 2: Continuous Integration (CI)

Module 3: Continuous Deployment (CD)

Module 4: Advanced CI/CD Practices

Module 5: Implementing CI/CD in Real Projects

Module 6: Tools and Technologies

Module 7: Practical Exercises

Module 8: Additional Resources

© Copyright 2026. All rights reserved