Compatibility Reference
Reference documentation for Protean's IR compatibility checking system -- the rules that classify changes as safe or breaking, the three-tier taxonomy, and the deprecation lifecycle.
For the how-to guide on setting up compatibility checks, pre-commit hooks, and CI integration, see Compatibility Checking.
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 for the full rationale):
Tier 1: Surface breaks
Renamed classes, moved imports, changed signatures.
Mitigation: Introduce the new API alongside the old. The old API emits
DeprecationWarning with a specific removal version and delegates to the
new implementation. Minimum survival: 2 minor versions.
import warnings
def old_method(self):
warnings.warn(
"old_method() is deprecated. Use new_method() instead. "
"Will be removed in v0.17.0.",
DeprecationWarning,
stacklevel=2,
)
return self.new_method()
Tier 2: Behavioral breaks
Same signature, different behavior.
Mitigation: Introduce new behavior behind a configuration flag, defaulting to old behavior. Minimum survival: 3 minor versions.
Transition timeline:
| Version | State |
|---|---|
| v0.N | New behavior is opt-in (flag defaults to old) |
| v0.N+1 | Warning emitted if flag is unset |
| v0.N+2 | Default flips to new behavior |
Tier 3: Structural breaks
Persistence format, event schema, serialization changes.
Mitigation: Version the schema or format explicitly. Document exact migration steps in the release's Upgrade Notes. Provide a migration script or CLI command where feasible.
The IR compatibility checker (protean ir diff) focuses on Tier 3
structural changes.
Deprecation lifecycle
When deprecating a domain element or field:
- Mark as deprecated with a
DeprecationWarningthat includes the removal version (see ADR-0004 for the deprecation pattern). - Keep the deprecated API for at least
min_versions_before_removalminor versions (default: 3, configurable in.protean/config.toml). - Add to
excludein.protean/config.tomlif the element should not trigger breaking change alerts during its deprecation period. - 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.
.protean/config.toml reference
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
[domains]
identity = "identity.domain"
catalogue = "catalogue.domain"
ordering = "ordering.domain"
[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. |
[domains]
Maps logical domain names to their module paths. When present, pre-commit
hooks iterate over all configured domains automatically -- no --domain
argument needed. Each domain's IR is stored under .protean/<name>/ir.json.
| Key | Type | Description |
|---|---|---|
<name> |
string | Dotted module path to the domain (e.g. "identity.domain"). The key is the logical name used as the subdirectory. |