# How the `diff` Command Detects Regressions in DESIGN.md Files

> Learn how the diff command detects regressions by comparing lint statistics in DESIGN.md files. Identify issues when later versions have more errors than earlier versions.

- Repository: [Google Labs Code/design.md](https://github.com/google-labs-code/design.md)
- Tags: how-to-guide
- Published: 2026-06-26

---

**The `diff` command detects regressions by comparing lint summary statistics between two versions of a DESIGN.md file, flagging a regression only when the *after* version contains more errors or warnings than the *before* version.**

The `diff` command in the [`google-labs-code/design.md`](https://github.com/google-labs-code/design.md/blob/main/google-labs-code/design.md) repository serves as an automated gatekeeper for design system changes. Unlike traditional diff tools that only compare text, this command parses both files into design system maps and evaluates whether modifications introduced new validation failures. Understanding how it detects regressions requires examining its four-stage pipeline implemented across the CLI utilities.

## The Regression Detection Pipeline

The command orchestrates file reading, linting, token comparison, and summary analysis to determine regression status. Each stage relies on specific utility functions defined in the source code.

### Reading Input Files with `readInput`

The process begins in [`packages/cli/src/commands/diff.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/commands/diff.ts) by loading both versions of the design file. The **`readInput`** function (defined in [`packages/cli/src/utils.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/utils.ts)) handles file paths or stdin (indicated by `-`) and returns the raw content without throwing parsing errors.

```typescript
const before = await readInput(beforePath);
const after = await readInput(afterPath);

```

This utility ensures the command can compare local files against previous versions or piped content from version control systems.

### Linting Both Versions

Both file contents pass through the **`lint`** function exported from [`packages/cli/src/linter/index.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/index.ts). This function parses the markdown, validates tokens, and returns a **`LintReport`** object containing two critical properties:

- **`designSystem`** – Parsed token maps for colors, typography, spacing, rounded corners, and components
- **`summary`** – Counts of `errors`, `warnings`, and `infos` found during validation

```typescript
const beforeReport = lint(beforeContent);
const afterReport = lint(afterContent);

```

The linting step establishes the baseline and comparison metrics for regression detection.

### Diffing Token Maps with `diffMaps`

While token changes are tracked for reporting purposes, they do not directly trigger regression flags. The **`diffMaps`** function (lines 58-84 in [`packages/cli/src/utils.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/utils.ts)) compares the `designSystem` maps from both reports and categorizes changes into three arrays:

- **`added`** – Keys present only in the *after* map
- **`removed`** – Keys present only in the *before* map  
- **`modified`** – Keys present in both but with different JSON-stringified values

The command calls `diffMaps` for each token category:

```typescript
tokens: {
  colors: diffMaps(beforeReport.designSystem.colors, afterReport.designSystem.colors),
  typography: diffMaps(beforeReport.designSystem.typography, afterReport.designSystem.typography),
  rounded: diffMaps(beforeReport.designSystem.rounded, afterReport.designSystem.rounded),
  spacing: diffMaps(beforeReport.designSystem.spacing, afterReport.designSystem.spacing),
  components: diffMaps(
    serializeComponents(beforeReport.designSystem.components),
    serializeComponents(afterReport.designSystem.components)
  )
}

```

For components, **`serializeComponents`** converts the nested `Map<string, ComponentDef>` structure into a plain object to enable structural comparison.

### Determining Regression Status

The actual regression detection occurs by comparing the `summary` objects from both lint reports. As implemented in [`packages/cli/src/commands/diff.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/commands/diff.ts) (lines 68-70), a regression is defined strictly by an increase in lint findings:

```typescript
regression: afterReport.summary.errors > beforeReport.summary.errors
  || afterReport.summary.warnings > beforeReport.summary.warnings,

```

The command calculates the delta between versions and sets the process exit code accordingly:

```typescript
process.exitCode = diff.regression ? 1 : 0;

```

If the *after* file contains more errors or warnings than the *before* file, the command exits with status `1`, signaling a regression. Otherwise, it exits with `0`.

## Practical Usage Examples

### Command Line Interface

Invoke the `diff` command to compare two design files directly:

```bash
npx @google/design.md diff DESIGN.md DESIGN-v2.md

```

The command outputs a JSON structure showing token changes and findings:

```json
{
  "tokens": {
    "colors": { "added": [], "removed": [], "modified": ["tertiary"] },
    "components": { "added": [], "removed": [], "modified": [] }
  },
  "findings": {
    "before": { "errors": 0, "warnings": 1, "infos": 1 },
    "after": { "errors": 1, "warnings": 2, "infos": 1 },
    "delta": { "errors": 1, "warnings": 1 }
  },
  "regression": true
}

```

Because the *after* state introduced additional errors and warnings, the `regression` flag is `true` and the process exits with code `1`.

### Programmatic Integration

You can replicate the regression logic in Node.js or TypeScript applications:

```typescript
import { readInput } from '@google/design.md/cli/src/utils.js';
import { lint } from '@google/design.md/cli/src/linter/index.js';
import { diffMaps } from '@google/design.md/cli/src/utils.js';

async function diffDesigns(beforePath: string, afterPath: string) {
  const before = await readInput(beforePath);
  const after = await readInput(afterPath);

  const beforeReport = lint(before);
  const afterReport = lint(after);

  const diff = {
    tokens: {
      colors: diffMaps(beforeReport.designSystem.colors, afterReport.designSystem.colors),
      typography: diffMaps(beforeReport.designSystem.typography, afterReport.designSystem.typography),
      spacing: diffMaps(beforeReport.designSystem.spacing, afterReport.designSystem.spacing),
    },
    regression:
      afterReport.summary.errors > beforeReport.summary.errors ||
      afterReport.summary.warnings > beforeReport.summary.warnings,
  };

  console.log(diff);
  process.exitCode = diff.regression ? 1 : 0;
}

```

This implementation mirrors the CLI behavior, enabling custom CI/CD integrations or automated design system monitoring.

## Key Source Files

Understanding the regression detection mechanism requires familiarity with these specific files in the [`google-labs-code/design.md`](https://github.com/google-labs-code/design.md/blob/main/google-labs-code/design.md) repository:

- **[`packages/cli/src/commands/diff.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/commands/diff.ts)** – Orchestrates the entire pipeline, including the regression logic at lines 68-70 that compares error and warning counts.
- **[`packages/cli/src/utils.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/utils.ts)** – Contains `readInput` and `diffMaps` (lines 58-84), which classify token changes into added, removed, or modified categories.
- **[`packages/cli/src/linter/index.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/index.ts)** – Re-exports the `lint` function that produces the `LintReport` and summary statistics.
- **[`packages/cli/src/linter/lint.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/lint.ts)** – Implements the parsing and rule evaluation that populates `summary.errors` and `summary.warnings`.
- **`packages/cli/src/linter/rules/*`** – Individual lint rules (such as `broken-ref` and `contrast-ratio`) whose counts drive the regression detection algorithm.

## Summary

- The `diff` command detects regressions by comparing **lint summary counts**, not token semantic changes, between two DESIGN.md versions.
- **Regression definition**: A regression occurs when the *after* version has more `errors` or `warnings` than the *before* version, as implemented in [`packages/cli/src/commands/diff.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/commands/diff.ts).
- The command uses **`readInput`** to load files and **`diffMaps`** to categorize token modifications, but these serve reporting rather than regression detection.
- Exit codes indicate status: `1` for regression detected, `0` for no regression.
- The **`lint`** function generates the summary statistics that ultimately determine whether a design change constitutes a regression.

## Frequently Asked Questions

### Does the diff command consider token changes as regressions?

No. The `diff` command does not flag semantic changes to design tokens (such as color value modifications or new component definitions) as regressions. According to the source code in [`packages/cli/src/commands/diff.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/commands/diff.ts), regressions are determined solely by comparing lint `summary` counts. Token diffs generated by `diffMaps` are provided for human review but do not influence the `regression` boolean or exit code.

### What exit codes does the diff command return?

The command returns exit code `1` when a regression is detected (meaning the *after* file has more errors or warnings than the *before* file), and exit code `0` when no regression is found. This behavior, implemented via `process.exitCode = diff.regression ? 1 : 0`, makes the command suitable for CI/CD pipelines where non-zero exit codes fail builds automatically.

### How does diffMaps determine if a token was modified?

The `diffMaps` function in [`packages/cli/src/utils.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/utils.ts) (lines 58-84) determines modifications by comparing JSON-stringified values of keys present in both the *before* and *after* maps. If `JSON.stringify(beforeValue) !== JSON.stringify(afterValue)`, the key is classified as `modified` rather than `added` or `removed`. This approach handles complex nested objects and ensures structural equality checks.

### Can the diff command compare files from stdin?

Yes. The `readInput` utility in [`packages/cli/src/utils.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/utils.ts) accepts `-` as a file path indicator, allowing the command to read from stdin. This enables piping scenarios such as `git show HEAD:DESIGN.md | npx @google/design.md diff DESIGN.md -` to compare the current working file against the previous Git version.