Frontend Learning Kit

Developer Experience Engineering

Developer experience is not about making developers happy. It's about reducing the time between "I have an idea" and "it's in production".


What DX Engineering Actually Is

Developer experience engineering is the practice of systematically identifying and eliminating friction in developer workflows.

The key word is systematically. Anyone can notice that something is slow or annoying. A DX engineer turns that observation into a measurable problem, designs a solution at the platform level, ships it, and verifies the impact.

The difference:


Measuring DX

You can't improve what you can't measure. Before fixing anything, instrument your workflows.

DORA Metrics

The four DORA metrics are the standard for engineering throughput:

MetricWhat it measuresElite benchmark
Deployment frequencyHow often code shipsOn-demand / multiple per day
Lead time for changesCommit β†’ productionLess than 1 hour
Change failure rate% of deploys that cause incidents0–5%
MTTR (mean time to restore)How fast you recoverLess than 1 hour

Developer-Specific Signals

Beyond DORA, track signals closer to the developer's daily experience:

Experience Surveys

Quantitative metrics miss the "why". A short monthly survey (5 questions max) surfaces friction that numbers can't:


CI/CD Pipeline Optimisation

Anatomy of a Slow Pipeline

Before optimising, profile. Most CI slowness comes from a small number of culprits:

  1. Cold starts β€” pulling Docker images, cloning the repo, installing deps
  2. Serial execution β€” steps that could run in parallel are running sequentially
  3. Test flakiness β€” tests that fail randomly cause retries and inflate average times
  4. Unnecessary work β€” running full test suites when only docs changed

Caching

The single highest-impact optimisation in most pipelines:

# GitHub Actions example
- uses: actions/cache@v4
  with:
    path: |
      ~/.npm
      node_modules
      .next/cache
    key: ${{ runner.os }}-${{ hashFiles('package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-

What to cache:

Parallelisation

jobs:
  lint:
    runs-on: ubuntu-latest
    steps: ...

  test:
    runs-on: ubuntu-latest
    steps: ...

  type-check:
    runs-on: ubuntu-latest
    steps: ...

  # All three run in parallel β€” no `needs:`

Change Detection

Don't run everything on every commit:

- name: Detect changed packages
  uses: dorny/paths-filter@v3
  id: changes
  with:
    filters: |
      ui:
        - 'packages/ui/**'
      api:
        - 'packages/api/**'

- name: Test UI
  if: steps.changes.outputs.ui == 'true'
  run: npm test --workspace=packages/ui

Local Development

The "Time to First Change" Metric

How long does it take a new developer to make a change and see it in their browser? This should be under 10 minutes. If it's longer, that's a platform problem.

The checklist:

API Mocking with MSW

Mock Service Worker intercepts requests at the network level β€” no need to mock fetch or axios.

// src/mocks/handlers.ts
import { http, HttpResponse } from 'msw';

export const handlers = [
  http.get('/api/user', () => {
    return HttpResponse.json({ id: '1', name: 'Test User' });
  }),
  http.post('/api/orders', async ({ request }) => {
    const body = await request.json();
    return HttpResponse.json({ id: 'order-123', ...body }, { status: 201 });
  }),
];

This means developers can work on frontend features without a running backend, and tests use the same mock setup.

Consistent Environments

Environment inconsistency ("it works on my machine") wastes more developer time than almost anything else.

Solutions in order of adoption difficulty:

  1. .nvmrc or .node-version β€” pins Node.js version, low friction
  2. mise or asdf β€” manages all tool versions (Node, pnpm, etc.)
  3. Dev containers (.devcontainer/) β€” full environment in a container
  4. Nix β€” hermetic builds, steep learning curve but ultimate consistency

Onboarding

The onboarding experience is a DX audit you can run repeatedly. Every few months, pair with a new joiner and observe β€” don't help, just watch and take notes on every friction point.

Good onboarding documentation includes:

Anti-patterns:


Tooling Recommendations

CategoryToolWhy
LintingESLint + @typescript-eslintType-aware rules catch real bugs
FormattingPrettier (no config)Zero bikeshedding
Pre-commit hookslint-staged + huskyFast feedback before CI
Commit messagescommitlint + conventional commitsEnables automated changelogs
API mockingMSWNetwork-level, works in browser and tests
Test runnerVitestFast, native ESM, compatible with Jest API
E2EPlaywrightMulti-browser, fast, good DX
BundlerViteDefault choice for new projects