What is CI/CD?
Automating the path from code commit to production deployment.
In the early days of software engineering, "Deploy Day" was an event. Every few months, the development team would hand over a massive bundle of code to the operations team. The operations team would manually upload files to a server, run database migrations, and inevitably spend the weekend fixing critical bugs that only appeared in the production environment.
CI/CD is the modern antidote to Deploy Day. It stands for Continuous Integration and Continuous Delivery/Deployment. It is the practice of automating the integration of code changes from multiple contributors into a single software project, and automatically delivering those changes to production.
Continuous Integration (CI)
Continuous Integration is the practice of merging all developers' working copies to a shared mainline (like the main branch in Git) several times a day.
The core rule of CI is that every commit must be verified by an automated build and test suite. This prevents "integration hell"—the situation where Developer A's new feature accidentally breaks Developer B's code.
A Standard CI Pipeline
- Commit: A developer pushes code to a feature branch and opens a Pull Request (PR).
- Linting: The CI server checks the code against style guidelines (e.g., ESLint, Prettier).
- Build: The CI server compiles the code (e.g., TypeScript to JavaScript) to ensure there are no syntax errors.
- Test: The CI server runs Unit Tests, Integration Tests, and E2E Tests.
- Report: If any step fails, the PR is blocked from being merged. If all steps pass, the code is considered safe to merge.
Continuous Delivery vs. Continuous Deployment (CD)
While CI ensures the code is correct, CD ensures the code gets to the users. The "CD" acronym actually stands for two different concepts:
Continuous Delivery
In Continuous Delivery, every change that passes the CI pipeline is automatically built, packaged (e.g., as a Docker container), and prepared for release. However, a human must explicitly authorize the final push to production (often by clicking a "Deploy" button). This is common in highly regulated industries like banking or healthcare.
Continuous Deployment
Continuous Deployment takes automation one step further: there is no human intervention. If the code passes the automated tests, it is automatically pushed to the live production servers. Companies like Amazon and Netflix use this to deploy code thousands of times a day.
GitHub Actions: The Modern CI/CD Tool
Historically, teams used standalone CI/CD servers like Jenkins, CircleCI, or TravisCI. Today, many teams have migrated to GitHub Actions because it integrates CI/CD directly into the code repository.
You define a "Workflow" using a YAML file placed in the .github/workflows directory. GitHub provides the virtual machines (runners) to execute your commands.
# .github/workflows/ci.yml
name: Node.js CI
# Trigger the workflow on pushes to the main branch
on:
push:
branches: [ "main" ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
# 1. Check out the repository code
- uses: actions/checkout@v3
# 2. Set up Node.js
- name: Use Node.js 18.x
uses: actions/setup-node@v3
with:
node-version: 18.x
# 3. Install dependencies
- run: npm ci
# 4. Run tests
- run: npm test Testing Strategies in CI/CD
A CI/CD pipeline is only as good as its tests. If your tests don't catch bugs, you are simply automating the deployment of broken code. A robust pipeline usually incorporates the Testing Pyramid:
- Unit Tests (Bottom): Fast, isolated tests that check individual functions or components. You should have thousands of these. (e.g., Jest, Vitest).
- Integration Tests (Middle): Tests that verify how different components work together, often involving a test database.
- End-to-End (E2E) Tests (Top): Slow, expensive tests that spin up a real browser and interact with the application exactly like a human user would. You should have a few of these covering critical paths like login and checkout. (e.g., Playwright, Cypress).