Developer DevOps & CI/CD Guide#

Table of Contents#

  1. 1. Shift-Left Philosophy: Dev vs SRE/Platform
  2. 2. CI/CD Workflows: GitHub Actions & Automations
  3. 3. The 12-Factor App in Practice
  4. 4. Developer Observability: Logs, Traces & Metrics
  5. 5. Deployment Strategies: Rolling, Blue/Green & Canary
  6. 6. Local-to-Production Parity with Docker Compose

1. Shift-Left Philosophy: Dev vs SRE/Platform#

Traditionally, developers wrote software and "threw it over the wall" to operations teams to run. DevOps for Developers is about owning your code from the moment you write your first unit test to when it runs in production, without having to configure bare-metal networks or cloud subnets.

The Developer's Scope

  1. Continuous Integration: Automated test suites, linting, security audits on every pull request.
  2. Containerized Packaging: Creating fast, reproducible, and minimal container images with multi-stage Docker builds.
  3. Environment Parity: Running identical dependencies locally in Docker Compose as in cloud staging/production.
  4. Application Observability: Emitting structured JSON logs, trace IDs, and business/runtime metrics to diagnose bugs in minutes instead of hours.

2. CI/CD Workflows: GitHub Actions & Automations#

Continuous Integration (CI) guarantees that code committed to a repository builds cleanly and passes all tests before merging. Continuous Delivery/Deployment (CD) automatically packages and deploys merged code to staging or production.

Below is a production-grade GitHub Actions workflow that runs linting, tests, builds a multi-stage Docker image, and publishes it to a container registry:

name: CI/CD Pipeline

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'
          cache: 'pip'

      - name: Install Dependencies
        run: |
          pip install -r requirements.txt
          pip install pytest ruff

      - name: Lint Code
        run: ruff check .

      - name: Run Unit Tests
        run: pytest tests/ -v

  build-and-push:
    needs: test
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and Push Docker Image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            ghcr.io/${{ github.repository }}:latest
            ghcr.io/${{ github.repository }}:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

3. The 12-Factor App in Practice#

The 12-Factor App methodology is the blueprint for modern, cloud-native applications. Here are the core factors developers interact with daily:

  1. Codebase: One codebase tracked in revision control, many deploys (dev, staging, prod).
  2. Explicit Dependencies: Explicitly declare and isolate dependencies (e.g. package-lock.json, poetry.lock, requirements.txt). Never assume system-wide libraries.
  3. Config in Environment: Store configuration that varies between deploys (database credentials, API endpoints) in environment variables, never hardcoded in source code.
  4. Backing Services as Attached Resources: Treat databases, message queues, and SMTP servers as attached resources accessed via URLs/credentials.
  5. Stateless Processes: Execute the app as one or more stateless processes. Never store session state on the local filesystem (use Redis or database). Any container can be destroyed or recreated at any moment without data loss.
  6. Port Binding: The app is completely self-contained and exports HTTP/gRPC by binding to a port (e.g. app.listen(8080)).
  7. Disposability: Maximize robustness with fast startup and graceful shutdown upon receiving SIGTERM.
  8. Dev/Prod Parity: Keep development, staging, and production as similar as possible using containerization.

4. Developer Observability: Logs, Traces & Metrics#

Platform engineers care about server CPU and disk space. Developers care about application observability: understanding why a request failed or took 4 seconds.

Structured JSON Logging

Never emit unformatted plain text strings like print("User logged in"). Modern log aggregation engines (Datadog, Loki, CloudWatch) parse structured JSON:

{
  "timestamp": "2026-09-08T06:15:00.123Z",
  "level": "INFO",
  "message": "Processed payment transaction",
  "requestId": "req-98f2-4bc1",
  "userId": "usr-10492",
  "durationMs": 42.8,
  "service": "payment-api"
}

Distributed Tracing & Trace Context

In microservices or multi-step agent pipelines, a single user click might touch 4 services. With OpenTelemetry and W3C trace context, a unique traceparent header is forwarded along every HTTP request, allowing you to see an end-to-end flame graph of execution:

# W3C Traceparent Header Format:
# version - trace_id (32 hex) - parent_span_id (16 hex) - trace_flags
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01

5. Deployment Strategies: Rolling, Blue/Green & Canary#

How your application updates in production directly affects code design, database migrations, and backward compatibility:

1. Rolling Update

New pods/containers are started gradually while old ones are terminated. Developer requirement: Version N and Version N+1 of your code will run concurrently for a few minutes. All database schema migrations must be backward-compatible (e.g. never drop a database column in the same release you stop using it).

2. Blue / Green Deployment

Two identical production environments exist. Blue runs live traffic (v1). Green is deployed with v2 and verified with smoke tests. Once verified, the router/load balancer instantly flips 100% of traffic from Blue to Green. Rollback is an instantaneous flip back.

3. Canary Release

A small percentage of real user traffic (e.g. 5%) is routed to the new version (the "canary"). If error rates or latency spike, the canary is rolled back automatically before impacting all users.

6. Local-to-Production Parity with Docker Compose#

The "it works on my machine" problem is solved with Docker Compose. A developer can spin up their application, database, Redis cache, and mock APIs with one command: docker compose up.

version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - "3000:3000"
    volumes:
      - .:/app               # Hot-reloading: changes in code reflect immediately!
      - /app/node_modules     # Prevent local host modules from overriding container
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgres://dev:secret@postgres:5432/appdb
      - REDIS_URL=redis://redis:6379
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started

  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: dev
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: appdb
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U dev -d appdb"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  pgdata: