CI/CD Pipeline Design for Modern Web Applications: From Push to Production in Minutes
A well-designed CI/CD pipeline is force multiplier for engineering teams. It catches bugs before they reach production, ensures consistent quality standards, and enables teams to ship with confidence multiple times per day. Here's how we design pipelines for production web applications.
Pipeline Architecture
Our standard pipeline follows this flow:
Push → Lint & Type Check → Unit Tests → Build → Integration Tests → Preview Deploy → Staging → Production
Each stage acts as a quality gate. If any stage fails, the pipeline stops and the team is notified immediately.
Stage 1: Static Analysis (30 seconds)
The fastest feedback loop catches formatting, linting, and type errors:
# .github/workflows/ci.yml
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run type-check # tsc --noEmit
Stage 2: Unit Tests (1-3 minutes)
Run your test suite with coverage reporting. Set coverage thresholds to prevent regression:
test:
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: 'npm'
- run: npm ci
- run: npm run test -- --coverage --ci
- uses: codecov/codecov-action@v4
with:
fail_ci_if_error: true
minimum_coverage: 80
Stage 3: Build & Preview Deploy (2-4 minutes)
For every pull request, deploy a preview environment. This gives reviewers a live URL to test changes before merging:
preview:
runs-on: ubuntu-latest
needs: [lint, test]
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run build
- uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
Stage 4: Database Migrations
Database schema changes require special handling. Never run migrations automatically in production without review:
- Development: Apply migrations automatically on push to develop
- Staging: Apply migrations on merge to main, validate before production
- Production: Apply after manual approval, with rollback plan documented
migrate-staging:
runs-on: ubuntu-latest
needs: [build]
if: github.ref == 'refs/heads/main'
environment: staging
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx prisma migrate deploy
env:
DATABASE_URL: ${{ secrets.STAGING_DATABASE_URL }}
Stage 5: Production Deployment
Production deployments should be zero-downtime. For Next.js on Vercel, this is handled automatically. For self-hosted deployments, implement blue-green or rolling updates.
Key Principles
- Fast feedback: Static analysis completes in under 30 seconds. Developers shouldn't wait more than 5 minutes for a full CI pass.
- Immutable artifacts: Build once, deploy the same artifact to staging and production.
- Preview environments: Every PR gets a live URL. This dramatically improves review quality.
- Automated rollbacks: If production health checks fail after deployment, automatically roll back to the previous version.
- Secrets management: Never hardcode secrets. Use GitHub Secrets, Vault, or your cloud provider's secret manager.
A great CI/CD pipeline pays for itself within weeks. The confidence to ship frequently, the automatic quality gates, and the reduced manual deployment overhead compound into a significant competitive advantage.