CI/CD Pipeline Setup: From Code Push to Production Deploy
A well-designed CI/CD pipeline catches bugs early, enforces code quality, and deploys with confidence. This guide walks through building a pipeline from scratch with lint, test, build, and deploy stages.
Key Takeaways
- Continuous Integration (CI) means every code change triggers automated checks — linting, type checking, unit tests, integration tests.
- Tools: Ruff (Python), ESLint (JavaScript), Biome (JS/TS), golangci-lint (Go)
- run: pip install ruff && ruff check .
- Fail fast — run cheap checks before expensive ones.
What CI/CD Means in Practice
Continuous Integration (CI) means every code change triggers automated checks — linting, type checking, unit tests, integration tests. Broken code is detected within minutes, not days. Continuous Deployment (CD) extends this by automatically deploying code that passes all checks to staging or production.
Pipeline Stages
Stage 1: Lint and Format
The fastest checks run first. Linting catches syntax errors, unused imports, and style violations in seconds. Formatting verification ensures consistent code style across the team. If linting fails, there's no point running slower tests.
Tools: Ruff (Python), ESLint (JavaScript), Biome (JS/TS), golangci-lint (Go)
Stage 2: Type Checking
Static type analysis catches type errors without executing code. This stage runs in parallel with linting since they're independent.
Tools: mypy (Python), TypeScript compiler, Flow (JavaScript)
Stage 3: Unit Tests
Unit tests verify individual functions and classes in isolation. They should complete in under 2 minutes for fast feedback. Mock external dependencies (databases, APIs) to keep unit tests fast and deterministic.
Stage 4: Integration Tests
Integration tests verify that components work together correctly — database queries return expected results, API endpoints respond correctly, and background jobs process as expected. These are slower (5-15 minutes) and may require database fixtures or Docker containers.
Stage 5: Build
Compile the application, build Docker images, bundle frontend assets, and generate static files. The build output is an artifact that can be deployed to any environment.
Stage 6: Deploy
Deploy the build artifact to the target environment. For staging, this may be automatic. For production, add a manual approval gate. Post-deploy, run smoke tests to verify the deployment is healthy.
Pipeline Configuration Example
# GitHub Actions example
name: CI/CD
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pip install ruff && ruff check .
test:
needs: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pip install -r requirements.txt
- run: pytest --cov
Key Principles
Fail fast — run cheap checks before expensive ones. Parallelize where possible — lint and type-check can run simultaneously. Cache aggressively — cache dependencies between runs to avoid re-downloading. Keep it green — a failing pipeline should be treated as a team priority, not background noise.