, , ,

CI/CD Explained: How Code Ships Without Anyone Pushing Buttons (DevOps for Beginners, Part 7)

CI/CD explained for beginners: what continuous integration and delivery actually automate, how a pipeline file works, and why your build passes locally but fails in CI.

12 min read
Before you start
To try the hands-on steps you will need a free GitHub account to run the pipeline. Everything here is free.
12 min read
Before you start
To try the hands-on steps you will need a free GitHub account to run the pipeline. Everything here is free.
DevOps for Beginners · Part 7 of 18
Bottom line
CI/CD is a robot that watches your Git repo. On every push it builds your code, runs your tests, and if everything is green it can ship the result. The pipeline is a file that lives in your repo, so the process is versioned and identical every time. It turns deploying from a tense manual ritual into something that happens quietly, many times a day.

Who this is for: a college passout or career switcher who has heard CI/CD in every job description and wants to know what the pipeline actually does, not just what the letters stand for.

It is 5pm on a Friday. The deploy is manual: copy the files up, run three commands in the right order, restart the service, and hope. Someone forgets step two and the site goes down for the weekend. Every team has lived this, and CI/CD is the answer they all reached. Instead of a human running the same fragile steps by hand, a machine runs them the same way every time, triggered by nothing more than a Git push.

Part 5 put your code in Git, and Part 3 showed the loop from push to production. This part fills in the automation that connects them. CI/CD stands for continuous integration and continuous delivery, and the two halves do different jobs. The clearest way to start is to see the three terms people use side by side.

TermWhat it automatesWho presses the last button
Continuous integrationBuild and test on every pushNobody, it just runs
Continuous deliveryEverything up to a ready-to-ship buildA human approves the release
Continuous deploymentAll the way to productionNobody, green means live

Continuous integration catches the break in minutes

Continuous integration means every change gets built and tested automatically, the moment it lands, on a clean machine. The point is speed of feedback. A bug caught by a machine ninety seconds after you push is cheap to fix, because the change is fresh in your mind. The same bug caught by a customer a week later is expensive and embarrassing. Here is a real pipeline that runs tests on every push, written for GitHub Actions and saved as .github/workflows/ci.yml.

name: ci-cd
on:
  push:
    branches: [main]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '22'
      - run: npm ci
      - run: npm test
  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: ./deploy.sh

Read the shape. There are two jobs, test and deploy. The deploy job has needs: test, which means it only runs if the test job passed. That one line is the safety rail: broken code never reaches the deploy step. When a test fails, the run stops and the log tells you exactly what went wrong.

Run npm test
FAIL  ./sum.test.js
  x adds two numbers together
    expected 5, received 4
Tests: 1 failed, 2 passed, 3 total
Error: Process completed with exit code 1.

That exit code 1 is the whole mechanism in one line. In the shell, a command that succeeds exits with code 0, and any other number means failure. The pipeline watches that number after every step. Zero and it continues. Anything else and it stops the run and paints it red. Your tests, your build, your linter, all of them report success or failure through that same exit code, which is why the pipeline can chain any tools together without knowing what they do.

The pipeline is a file in your repo

The important idea is not the specific tool. It is that the whole process lives as a file next to your code, versioned in Git like everything else. Change the pipeline and that change is reviewed and tracked. Nobody can quietly alter how deploys work by clicking around a web console, because the truth is the file. A typical pipeline runs through a handful of stages in order, and any red stage stops the line.

CheckoutBuildTestScanPackageDeployEach stage must pass before the next begins.
StageWhat runsIf it fails
CheckoutPull the code onto a clean runnerRare, usually a config error
BuildCompile or bundle the appA syntax or dependency error
TestRun automated checksA bug, the run stops
ScanCheck for known vulnerabilitiesA risky dependency, flagged
DeployShip the passing buildOnly reached if all above passed

Delivery vs deployment: who presses the button

The only real difference between continuous delivery and continuous deployment is whether a human approves the last step. In continuous delivery, the pipeline does all the work and produces a build that is ready to go, then waits for a person to click deploy. In continuous deployment, there is no click. A green pipeline goes straight to production. Both are valid, and the right choice depends on how confident you are in your tests and how much a bad deploy would cost.

build and testready buildmanual gatedelivery onlyproductiondeployment skips the gate: green goes live automatically

Why your pipeline passes locally but fails in CI

This is the failure that confuses every beginner, so learn it now. Your tests pass on your laptop, you push, and the pipeline goes red. The reason is almost always the same: the CI runner is a brand new, empty machine. It has none of the tools, files, or environment variables that quietly live on your laptop. A package you installed months ago and forgot, a config file you never committed, a secret sitting in your local shell, none of that exists on the runner. The pipeline is honest in a way your laptop is not.

That honesty is a feature, not an annoyance. If your app only builds on your machine, it does not really build, it just happens to run where you have been lucky. The fix is to make sure everything the app needs is declared in the repo: dependencies in a lock file, config in committed files, and secrets injected by the pipeline rather than assumed. When CI catches this, it is catching a real problem before a new teammate or a production server hits the same wall.

Gotcha: the green check that lies
A passing pipeline only proves your tests passed. It does not prove your tests are any good. A repo with one trivial test will show a reassuring green check on every push while real bugs sail straight through. New teams sometimes chase the green badge and forget that the badge is only as honest as the checks behind it. When you inherit a pipeline, the useful question is not is it green, it is what does green actually guarantee here.

Pull requests are where CI earns its keep

The pipeline does not only run when code reaches main. It runs on the pull request, before the merge, and this is where continuous integration quietly prevents most disasters. Recall the loop from Part 5: you branch, commit, and open a pull request. The moment that request opens, the pipeline runs the whole test suite against your proposed change and reports back right on the request. A teammate reviewing your code can see, at a glance, whether it even passes before they spend time reading it.

Teams then turn on a rule called branch protection, which says a pull request cannot be merged into main unless its checks are green. This is the single most effective guardrail in DevOps, and it costs one setting. Broken code physically cannot reach the main branch, because the merge button stays disabled until the pipeline passes. No amount of hurry or good intentions can override it. That is how a team of strangers keeps a shared branch always working.

open PRchecks rungreen requiredgatemerge to mainRed checks keep the merge button locked. Main stays working.

For a beginner, this changes how it feels to contribute. You are not one careless push away from taking down the team. The pipeline is a safety net under every change, and the worst a failing test can do is keep your own branch from merging until you fix it. That safety is exactly what lets teams move fast without breaking things, and it is why setting up CI is usually the first thing a new project does after the first commit.

Do you really need deploy on every commit?

Conference talks make full continuous deployment to production sound like the only real DevOps. For a beginner or a small team, that is the wrong first goal. Getting solid continuous integration in place, build and test on every push, is where almost all the value is, and it is the part that stops bad code early. Automated deployment straight to production is a later step you earn once your tests are trustworthy and your monitoring can catch a bad release fast. Many excellent teams run rock-solid CI and deploy to production with a deliberate human click, and they are not behind. They are being sensible about risk.

So set your first target as green tests on every push and an easy, repeatable deploy you trigger yourself. That alone removes the Friday-evening fear. Chase fully automatic production deploys after you trust what green means, not before.

Interview question you will meet
What is the difference between continuous delivery and continuous deployment? The short, correct answer: both automate everything up to production, but delivery stops at a manual approval before the release, while deployment ships to production automatically the moment the pipeline is green. Add that the choice is about risk and test confidence, not about one being more advanced, and you sound like someone who has actually run pipelines rather than memorized definitions.
Why this matters on day one
On most teams your first real contribution rides through a pipeline you did not write. Knowing how to read a failed run, find the red step, open its log, and see the exact command that returned a non-zero exit code makes you self-sufficient fast. The junior who says the deploy job failed because the test job failed on this assertion, here is the line is instantly more useful than the one who just says the build is broken. Reading pipeline logs is a daily skill, and it is learnable in an afternoon.
Try it yourself
Free, about twenty minutes. Make a GitHub repo with a tiny project and one real test, then add the ci.yml from above with just the test job. Push and watch the Actions tab. Now change the test so it fails, push again, and read the log until you find the failing assertion and the exit code 1 line. Then fix it and watch the run go green. How to check you did it right: the commit shows a red x when the test is broken and a green check when it passes, and you can point to the exact log line that caused each result.
In practice
When you join a team, the fastest way to understand how they ship is to open the pipeline file, usually under .github/workflows or a .gitlab-ci.yml at the repo root. Read it top to bottom once. It tells you what gets built, what tests must pass, where secrets come from, and whether deploys are automatic or gated. That single file is the most honest documentation of how the team actually releases software, far more reliable than a wiki page someone wrote a year ago and never updated.

CI/CD questions beginners bring to interviews

Is CI/CD a specific tool?
No, it is a practice, and many tools implement it. GitHub Actions, GitLab CI, and Jenkins are common ones. They all do the same core job: watch the repo, run steps on a fresh machine, and report pass or fail. Learn the concept once and switching tools is mostly learning new file syntax.

What is a runner?
The machine the pipeline runs on. When you push, the CI service spins up a clean runner, checks out your code, and executes your steps on it. The runner is thrown away after the run, which is why every build starts from a known, empty state and why local-only setup fails there.

Where do secrets like API keys go?
Never in the pipeline file, because that file is in Git for everyone to read. CI tools have an encrypted secrets store, and the pipeline pulls the value in as an environment variable at run time. Committing a key to the repo is one of the most common and costly beginner mistakes, so treat the pipeline file as public.

How long should a pipeline take?
Fast enough that people wait for it, ideally under about ten minutes for the test stage. A slow pipeline gets ignored or bypassed, which defeats the purpose. Caching dependencies and running independent jobs in parallel are the usual ways teams keep it quick as a project grows.

You can now read and reason about a pipeline, which is the beating heart of daily DevOps work. Next up is Part 8 on containers and Docker, the technology that packages your app so it runs the same on the runner, on your laptop, and in production.

New here? Start with Part 3 on the DevOps loop and Part 5 on Git, which the pipeline watches. The Python for Beginners series pairs well if you are new to writing the code being tested.

DevOps for Beginners · Part 7 of 18
« Previous: Part 6  |  Complete Guide  |  Next: Part 8

References

GitHub Actions documentation
GitLab: What is CI/CD?
Martin Fowler: Continuous Integration

About The Author


Discover more from Journal of Intelligent Infrastructure – By Dr Pranay Jha

Subscribe to get the latest posts sent to your email.

Leave a Reply

Your email address will not be published. Required fields are marked *

Architect’s Toolkit

About the Author

Dr. Pranay Jha is a Cloud and AI Consultant with 18+ years of experience in hybrid cloud, virtualization, and enterprise infrastructure transformation. He specializes in VMware technologies, multi-cloud strategy, and Generative AI solutions. He holds a PhD in Computer Applications with research focused on Cloud and AI, has published multiple research papers, and has been a VMware vExpert since 2016 and a VMUG Community Leader.

Discover more from Journal of Intelligent Infrastructure - By Dr Pranay Jha

Subscribe now to keep reading and get access to the full archive.

Continue reading