Skip to content

Compatibility checking

Protean's IR (Intermediate Representation) tooling helps you detect breaking changes to your domain model before they reach production. This guide covers the .protean/ directory, configuration, pre-commit hooks, and CI integration.


The .protean/ directory

The .protean/ directory is your project's materialized IR workspace. It contains:

.protean/
├── ir.json        # Materialized IR snapshot of your domain
└── config.toml    # Optional configuration for compatibility checks

Generate the IR snapshot with:

protean ir show --domain my_app.domain > .protean/ir.json

Commit .protean/ir.json to version control. It serves as the baseline for detecting changes between releases.


Configuration

Create .protean/config.toml to customize compatibility checking behavior. All settings are optional --- sensible defaults apply when the file is absent.

[compatibility]
strictness = "strict"  # "strict" | "warn" | "off"
exclude = ["myapp.internal.LegacyEvent"]

[compatibility.deprecation]
min_versions_before_removal = 3

[staleness]
enabled = true

[compatibility]

Key Type Default Description
strictness string "strict" "strict" exits non-zero on breaking changes. "warn" reports but allows. "off" skips checking entirely.
exclude list of strings [] Fully-qualified names of elements to exclude from compatibility checks.

[compatibility.deprecation]

Key Type Default Description
min_versions_before_removal integer 3 Minimum minor versions a deprecated element must survive before removal.

[staleness]

Key Type Default Description
enabled boolean true Whether the staleness check (protean ir check) is active. Set to false to skip.

Breaking change rules

Protean classifies changes to persisted domain elements using these rules:

Change Classification
Add optional field (or with default) Safe
Add required field without default Breaking
Remove field from any persisted element Breaking
Change field type Breaking
Remove an element Breaking
Add a new element Safe
Visibility public to internal Breaking
Visibility internal to public Safe
Change __type__ string Breaking

These rules apply to all persisted elements: aggregates, entities, value objects, commands, events, database models, and projections.

Three-tier breaking change taxonomy

Protean follows a tiered approach to breaking changes (see ADR-0004):

  • Tier 1 (Surface): Renamed classes, moved imports, changed signatures. Mitigated with DeprecationWarning surviving 2+ minor versions.
  • Tier 2 (Behavioral): Same signature, different behavior. Mitigated with opt-in flags over 3 minor versions.
  • Tier 3 (Structural): Persistence format, event schema, serialization changes. Mitigated with versioned schemas and migration documentation.

The IR compatibility checker focuses on Tier 3 structural changes.


CLI commands

protean ir check

Compare the live domain against the materialized IR:

protean ir check --domain my_app.domain

Exit codes: 0 (fresh), 1 (stale), 2 (no IR found).

protean ir diff

Compare two IR snapshots with full breaking-change classification:

# Auto-baseline: compare live domain against .protean/ir.json
protean ir diff --domain my_app.domain

# Compare against a specific git commit
protean ir diff --domain my_app.domain --base HEAD

# Compare two explicit files
protean ir diff --left baseline.json --right current.json

Exit codes: 0 (no changes), 1 (breaking changes), 2 (non-breaking only).

When strictness = "warn", breaking changes are reported but the exit code is 0. When strictness = "off", the command exits 0 immediately.


Pre-commit hooks

Protean ships two pre-commit hooks. Add them to your project's .pre-commit-config.yaml:

- repo: https://github.com/proteanhq/protean
  rev: v0.15.0  # use the version you depend on
  hooks:
    - id: protean-check-staleness
      args: [--domain=myapp.domain]
    - id: protean-check-compat
      args: [--domain=myapp.domain]

protean-check-staleness

Blocks the commit if .protean/ir.json is out of date. Regenerate with:

protean ir show --domain my_app.domain > .protean/ir.json

Respects staleness.enabled in config.toml.

protean-check-compat

Blocks the commit if breaking IR changes are detected against the baseline in HEAD. Respects compatibility.strictness and compatibility.exclude in config.toml.


CI integration

GitHub Actions

Add a compatibility check step to your CI workflow:

- name: Check IR compatibility
  run: |
    protean ir diff --domain myapp.domain --base origin/main

The command exits with code 1 on breaking changes, which fails the CI step.

pytest warning filters

Turn Protean deprecation warnings into test failures:

# pyproject.toml
[tool.pytest.ini_options]
filterwarnings = [
    "error::DeprecationWarning:protean.*",
]

This catches deprecated API usage during development rather than after a breaking release.


Deprecation lifecycle

When deprecating a domain element or field:

  1. Mark as deprecated with a DeprecationWarning that includes the removal version (see CLAUDE.md for the pattern).
  2. Keep the deprecated API for at least min_versions_before_removal minor versions (default: 3).
  3. Add to exclude in config.toml if the element should not trigger breaking change alerts during its deprecation period.
  4. Remove in a cleanup release after the survival window.

The protean ir diff command distinguishes expected removals (deprecated elements past their removal version) from unexpected removals.