CI/CD Pipelines
Automate your testing, building, and deployment. Never manually deploy again.
What is CI/CD?
Continuous Integration (CI) means every time someone pushes code, it is automatically tested. Linting, unit tests, integration tests - all run automatically. If anything fails, the team knows immediately.
Continuous Deployment (CD) means that once tests pass, the code is automatically deployed to production. No human clicking buttons. Push to main, tests pass, it is live.
Together, they eliminate manual work, catch bugs early, and let you ship faster with confidence.
GitHub Actions - The Modern Standard
GitHub Actions is built into GitHub. You define workflows in YAML files inside .github/workflows/. This example runs tests on every push and pull request, then deploys to your server when code lands on main.
name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run lint
- run: npm test
build-and-deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm run build
- name: Deploy to server
run: |
rsync -avz ./dist/ user@server:/var/www/app/Docker in CI/CD
A very common pattern is building a Docker image in CI and pushing it to a registry. Then your deployment step just pulls the new image and restarts the container.
name: Docker Build and Push
on:
push:
branches: [main]
jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
push: true
tags: myuser/myapp:latest,myuser/myapp:${{ github.sha }}Notice we tag with both "latest" and the git commit SHA. This gives you a stable tag and a way to trace exactly which commit is running.
Jenkins - The Enterprise Option
Jenkins is self-hosted, highly customisable, and has been around for years. You will encounter it at larger companies. Configuration lives in a Jenkinsfile.
pipeline {
agent any
stages {
stage('Install') {
steps {
sh 'npm ci'
}
}
stage('Test') {
steps {
sh 'npm test'
}
}
stage('Build') {
steps {
sh 'npm run build'
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
sh './deploy.sh'
}
}
}
}Best Practices
- - Keep pipelines fast. Developers should not wait 30 minutes for feedback
- - Store secrets in your CI tool, never in code or YAML files
- - Run tests in parallel when possible
- - Use caching (node_modules, Docker layers) to speed up builds
- - Always deploy the same artifact that was tested - do not rebuild for production
- - Set up notifications so the team knows when builds fail