Continuous Integration
What runs on every push, fail fast, a green main, and why you need to merge often.
What runs on every push, why a pipeline should fail early, why you keep
main green, and why you merge changes often.
The idea of CI in one sentence
Every developer merges their changes into a shared branch often and in small batches, and automation checks that nothing broke on every change.
"Continuous" here is about frequency: not once a week in one big chunk, but many times a day in small ones. Martin Fowler's article on CI names the lower bound: merge your changes into the shared branch at least once a day. The smaller the batch, the easier it is to find what broke the build.
What CI usually runs
A typical CI run on every push to a merge request:
checkout the code
-> install dependencies
-> linter / formatter (style, obvious mistakes)
-> build / compile (does it even build?)
-> unit tests (is the logic broken?)
-> other checks (types, security scan, bundle size)
Each step (GitLab calls it a job) is a command or a script. The CI system looks at its exit code: 0 means success, anything else means failure. If a job fails, the later stages usually don't run, the pipeline turns red, and the merge request can't be merged. This is the automated quality gate at the door.
A simple consequence follows. A script that swallows an error and returns 0 looks green to CI, even if everything inside broke. A check is only as good as how honestly it reports failure.
Fail fast
A good pipeline fails as early and as cheaply as possible. Fast checks go first: a linter finishes in seconds. Expensive ones go later: integration tests take minutes. There's no point running 10-minute tests if the code doesn't even compile.
The goal is for the developer to learn about the breakage within minutes after the push, while the context is still fresh, not a day later in code review.
Green main as a rule
The main branch (main/master) must always be in a working state that
passes its tests. A release can be built from it at any moment.
So:
- a direct push to
mainis forbidden, only through a merge/pull request, - the MR doesn't merge while CI is red,
- approval from a reviewer is often required too.
This is the same branch protection from the lesson on branches in the Git track. CI is its mandatory half: the rule "main is always green" holds only because the checks are automatic and can't be bypassed.
In GitLab these are two settings. Branch protection sets who can push and
merge into main; the default branch in a new project is already protected.
Blocking merges on a red pipeline is a separate setting: Settings → Merge
requests → Merge checks, the "Pipelines must succeed" checkbox. In GitHub the
same thing is done by the branch protection rule "Require status checks to
pass before merging".
Why merging often is safer
Counterintuitive: the more often you merge, the lower the risk. A big branch
that lives for two weeks drifts further and further from main. At merge
time you get a pile of conflicts and surprises. This is called merge hell.
The approach where everyone stays close to main and merges in small pieces
is called trunk-based development. Small MRs are easier to review, pass
CI faster, and conflict less often.
What the code needs for CI to work
CI isn't magic. It only runs what you wrote. For that to mean anything:
- tests must exist and be fast (nobody waits for slow ones),
- the build must be reproducible (pinned dependency versions in a lock file, images with an exact tag),
- checks must be deterministic.
That last point is worth unpacking. A flaky test passes and fails on the same code from run to run. The cause is almost always that the result depends on something other than the code: the current time, random data, the network, test run order, or an element order the language doesn't promise. The team quickly gets used to hitting "rerun", starts ignoring a red CI, and it becomes useless.
A flaky test isn't fixed by rerunning it or disabling it. You need to find what the result depends on and remove that dependency from the code or the test.
Common mistakes
- The CI script doesn't pass through the exit code. One of the commands
failed, but the script ran to the end and returned 0. The pipeline is
green, and the breakage ships to
main. - Expensive checks run first. Integration tests sit at the start, and you find out about a typo the linter would have caught in a second only ten minutes later.
- "Rerun it, it passed for me." A red run that passes on the second try isn't bad luck, it's a flaky test. It needs fixing.
- Fixing the test instead of the code. A test describes what the code promises. If the test is right and the code isn't, editing the test hides the bug.
- Merging once every two weeks. The longer a branch lives, the more the merge hurts.
What to remember
CI: merge often and in small batches, and automatically check every change.
A typical run: dependencies → linter → build → tests. If it fails, the MR doesn't merge. CI learns about failure from a nonzero exit code.
Fail fast: cheap checks before expensive ones, so breakage surfaces within minutes.
main always green: push only through an MR, can't merge while CI is red.
Frequent small merges (trunk-based) are safer than rare big ones: no merge hell.
What you'll need for the tasks
- Docker and bash: the pipeline in the labs runs locally, in a clean
python:3.12.7-alpinecontainer, the same way a CI server would. - Reading pipeline output: the
=== job: ... ===lines and the exit code at the end. - Understanding exit codes:
echo $?right after a command prints its exit code. - No Python knowledge needed: the project code is a dozen lines, readable as pseudocode.
- Lab 3 only needs a browser: the GitLab documentation sections on branch protection and merge checks.
The next lesson covers the second letter: how Continuous Delivery differs from Continuous Deployment.
Interview-style questions
Interview questions on this topic: 3
Included with practice access, $49 one-time. Get access
Answer out loud or in writing, then reveal the answer and honestly mark whether you knew it.
Done reading?
Mark the lesson as read to move your track progress forward.
Lesson marked as done.
Go through it with an AI agent
The agent explains anything unclear in plain words, gives you 3 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.
Tell the agent: use MCP izi, give me lesson cicd-basics-02-continuous-integration
Progress saved in this browser
Clear your history or open this on your phone, and the marks will be gone. Leave your email and they'll move to your account. No password needed.
Done. moved to your account. You can see your progress on any device. My progress