→ From Coder to Shipper
From Coder to Shipper · DevOps

CI/CD — automated build, test & deploy

One push to the repo, and a machine builds your app, tests it, and ships it — no humans, no 'I'll do it tonight'.

← / → to navigate · EN/VI switch below

The problem

Shipping was a scary, manual job

Before CI/CD, every deploy was an event: someone had to remember the steps, do them in order, and hope.

Manual steps, every time

Restart the server? Swap the files? Run the migration? The recipe lives in someone's head and in a wiki nobody updates.

Tests skipped under pressure

'It works on my machine, I'm sure.' Under deadline pressure, the test step is the first thing that quietly disappears.

Breakage found by users

The first person to notice a bug is a customer, on production, at 2 a.m. Not a test, not a teammate.

'I'll deploy it tonight'

Deploys pile up, changes get big, and every ship carries more risk than it should.

The idea

CI/CD in one breath

CI/CD = an automated pipeline: whenever code changes, the machine builds it, tests it, and deploys it — the same way, every time.

Pipeline — the conveyor belt

A recipe of steps that runs automatically on a machine in the cloud. You push code; the belt does the rest.

CI — Continuous Integration

On every commit: build the app and run the tests. Breakage is caught in minutes, not weeks.

CD — Continuous Delivery / Deployment

When the tests pass, push the result to staging or production — automatically, or with one click.

One push = one run

No special 'deploy day'. Every push to the repo triggers the same pipeline — small, frequent, boring. Boring is good.

CI/CD · the two halves

CI vs CD — check first, ship second

Two ideas, one pipeline. CI asks 'is it safe?'; CD says 'make it so'.

CI — the quality gate

Build + test on every commit. The moment your change breaks something, the pipeline turns red and the deploy stops.

Goal: small, frequent changes; breakage found in minutes.

CD — the shipping lane

A passing pipeline ends with deployment: the new version goes to staging (or straight to production). Always the same path.

Goal: the app is deployable at any moment — because it just was.

The core flow

push → build → test → deploy

One push to the repo, and the machine walks this belt from left to right. Any red step stops everything.

You
git push — one command, one commit
then you close the laptop
→
The pipeline — runs in the cloud
1checkout — fetch the exact commit
2install — deps from requirements.txt
3test — the whole test suite
4build — app → docker image
5deploy — new image onto the server
→
Result
✅ green — live in minutes
❌ red — deploy stops, you get the error
What it does

Five jobs, zero humans

Between 'git push' and 'it's live', the machine does these four things — every single time, in the same order.

1 · Checkout — get the code

The machine fetches the exact commit you pushed — same bytes, same versions, no 'which version was it again?'

2 · Install — set up the kitchen

A clean machine installs Python and your dependencies from requirements.txt. No 'it worked on my box' excuses.

3 · Test — the quality gate

Your test suite runs. One failing test = red pipeline = nothing ships. This is CI doing its job.

4 · Build + deploy — the shipping

The app becomes a container image, and the new image is deployed to the server. Staging first, production after — automatically.

In the wild · a green run

What a passing pipeline looks like

This is the whole 'deploy' — a log that scrolls by itself while you do something else.

ops@vps — zsh
Run #142 · build-test-deploy · branch: main
✓ 1. Checkout code (3s)
✓ 2. Install dependencies (41s)
✓ 3. Run tests (27s) — 128 passed
✓ 4. Build docker image (55s)
✓ 5. Deploy to staging (12s)
Pipeline finished green — staging is running the new version.
In the wild · a red run

And the run that saves you

A test fails — so the bad code never reaches the server. You get the error in minutes, not a customer at 2 a.m.

ops@vps — zsh
Run #143 · build-test-deploy · branch: main
✓ 1. Checkout code (3s)
✓ 2. Install dependencies (40s)
✗ 3. Run tests (21s) — 127 passed, 1 failed
FAILED test_login.py::test_empty_password — ValueError
Deploy step skipped — the bad code never left the pipeline.
01
Part 1 · Your first pipeline

Writing your first pipeline

On GitHub, the whole magic is one file: a few lines of YAML describing the steps.

Part 1 · the file

A whole pipeline in 15 lines of YAML

This file lives in your repo at .github/workflows/. GitHub runs it automatically every time you push to main.

.github/workflows/deploy.yml
# runs automatically on every push
name: build-test-deploy
on:
  push:
    branches: [main]

jobs:
  ship:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4         # 1. get the code
      - run: pip install -r requirements.txt  # 2. deps
      - run: pytest                       # 3. test
      - run: docker build -t myapp .      # 4. build image
      - run: ./deploy.sh                  # 5. ship it

on: push = when · jobs = what · steps = in what order.

Part 1 · how to read it

Three words you need to read it

Every GitHub Actions workflow is just these three ideas. The rest is vocabulary.

on: — the trigger

'When does this run?' on: push = every push to the repo. Other options: on a schedule, on a pull request, on a tag.

jobs: — the what

A job is a named chunk of work that runs on a fresh machine — runs-on: ubuntu-latest. Here, one job named ship does everything.

steps: — the order

Ordered commands, top to bottom: - uses: runs a ready-made step (checkout), - run: runs one shell command. The first failure stops the rest.

Part 1 · the last step

The deploy step, up close

The final step does the boring part — once written, it runs itself forever.

deploy.sh
# deploy.sh — runs on the server
docker pull myapp:latest      # newest image
docker stop web || true
docker rm web || true
docker run -d --name web -p 8080:5000 myapp:latest
curl -fsS http://127.0.0.1:8080/health

Stop the old container, start the new one, knock on the door to check it answers.

Part 1 · the toolbox

The three tools you'll actually meet

Same idea, different homes. This course uses GitHub Actions — the others are named so you recognize them in the wild.

ToolWhere it livesWhat to know
GitHub ActionsGitHub — home of this course's reposYAML files in your repo (.github/workflows/). Free for public repos. This course's pick.
GitLab CIGitLab — cloud or self-hostedSame idea, config in one .gitlab-ci.yml file. Common where companies self-host.
JenkinsYour own server — the veteranA program you install yourself, around since 2011. Pipelines in code or UI. Still everywhere in big companies.
The payoff

Why teams never go back

It's not about speed alone. It's about removing a whole class of scary human moments.

No more 'I'll deploy it tonight'

Deploys happen by the pipeline, not by a hero's calendar. You push; the belt handles the rest.

Every change takes the same road

Same steps, same machine, same order — for the 3-line tweak and the big feature. Repetition kills 'works on my machine'.

Fast, cheap feedback

Push → green or red in minutes. You find out your change broke something before a user ever can.

Small changes, big confidence

Because shipping is boring and automatic, changes stay small and frequent. That's how teams ship daily without fear.

Big picture

The conveyor belt from 'I pushed' to 'it's live'

One sentence about the whole topic: the machine walks the belt, you just push.

💻

You

git push

the only human step
push→
🏭

GitHub Actions

checkout → install → test → build → deploy

the conveyor belt
→minutes
🚀

The server

new version is live

automated · tested · repeatable

push → pipeline → live. No deploy days, no forgotten steps, no 'works on my machine'.

git push. That's your whole job now.

push → build → test → deploy, on every change. The belt runs; you ship.