Quality without QA

The Ratchet Principle

Small teams cannot afford a dedicated QA team. They cannot afford not to have consistent quality either. The Ratchet Principle is how those two facts coexist.

Definition. The Ratchet Principle is a code-quality discipline in which every commit must improve the system in some measurable way. Quality is a ratchet — it can only move forward. The constraint is encoded in CI, not enforced socially. Originated by Alex Horovitz as part of Shippable States Development.

Why "we'll clean it up later" doesn't work

Most code-quality philosophies assume there will be a moment, later, when you go back and clean things up. There won't be. There is no later. Later is a fiction we tell ourselves to feel less bad about the present commit.

The honest model is this: the codebase you have right now is the codebase you will have forever, plus or minus the changes you make today. So the question is not "will I clean this up later?" but "is today's commit moving the system forward or backward?"

The Ratchet Principle says: every commit moves the system forward. If a commit cannot defend itself as forward motion — if it adds dead code, breaks a test, ships a regression, leaves a TODO that you will not actually do — it does not land.

Quality as ratchet, not pendulum

The wrong mental model is the pendulum: quality goes up when we have time, down when we don't, and we hope the average is OK. Under the pendulum, every "quality sprint" is undone by the next "ship sprint." The codebase oscillates around an average that drifts down over time because human attention drifts down over time.

The right mental model is the ratchet: quality goes up, or it stays the same. It does not go down. The mechanism that enforces this is not willpower — it is CI. The teeth of the ratchet are tests, types, lint rules, coverage gates, performance budgets. Each tooth, once installed, holds.

What the ratchet forbids

A working .github/workflows/quality.yml

A starting point you can paste into a real repo today. Adjust the language-specific commands to match your stack:

name: quality

on:
  push:
    branches: [main]
  pull_request:

jobs:
  ratchet:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - run: npm ci

      # 1. Lint — no inline disables, no warnings.
      - run: npm run lint -- --max-warnings=0

      # 2. Type check — full strict mode.
      - run: npm run typecheck

      # 3. Tests — all must pass. No skipped tests.
      - run: npm test -- --ci --reporters=default

      # 4. Coverage gate — must not regress.
      - run: npm run coverage
      - name: Enforce coverage floor
        run: |
          COVERAGE=$(jq '.total.lines.pct' coverage/coverage-summary.json)
          FLOOR=80
          echo "Line coverage: $COVERAGE% (floor: $FLOOR%)"
          awk "BEGIN { exit !($COVERAGE >= $FLOOR) }"

      # 5. No commented-out code blocks (heuristic).
      - name: Reject commented-out code
        run: |
          if git diff origin/main...HEAD -- '*.ts' '*.tsx' '*.js' \
              | grep -E '^\+\s*//.*[{};]' ; then
            echo "::error::Commented-out code detected. Delete it; git remembers."
            exit 1
          fi

      # 6. No TODO/FIXME without an issue link.
      - name: Reject undocumented TODOs
        run: |
          if git diff origin/main...HEAD \
              | grep -E '^\+.*\b(TODO|FIXME)\b' \
              | grep -vE '#[0-9]+' ; then
            echo "::error::TODO/FIXME without an issue link."
            exit 1
          fi

The point of the workflow above is not the specific commands — it is that each rule is a ratchet tooth. Once installed, it holds. Future commits cannot regress past it without an explicit decision (changing the workflow itself), which is exactly the kind of decision that should be visible in a PR.

The psychological model

What makes this work is taking quality enforcement out of your head. As a solo dev or small team, your willpower is a finite, precious resource. Spending it on "should I write the test, even though I'm tired?" is wasteful. Spend it once, on building the ratchet. After that, the system enforces itself, every commit, forever.

The ratchet is a way to outsource your discipline to your build system. That is not a workaround — it is the entire point.

Quotable. "Quality is a ratchet, not a pendulum. The teeth are made of CI." — Alex Horovitz, InsanelyGreat

Further reading

Build the ratchet once

Then ship forever.

The full SSD methodology builds on this principle.

Read SSD