ADR-0003: Clean Breaks Over Backward Compatibility¶
Status: Accepted Date: 2026-03-20
Context¶
Dazzle is pre-1.0 software with a single primary user who is fully engaged with the development process. As the DSL, IR, and runtime APIs evolve, questions arise about how to handle interface changes:
- Should old function signatures be preserved with deprecation warnings?
- Should compatibility aliases re-export renamed symbols?
- Should new modules wrap old modules for gradual migration?
Standard open-source practice favours deprecation cycles to protect downstream users. However, that practice assumes:
- Many users on varying release schedules
- Maintainers without visibility into downstream usage
- Time and attention cost of coordinating migrations
None of these apply here. The primary user reviews every significant change before it ships.
Decision¶
Backward compatibility is not a requirement before v1.0.
The following rules apply:
- Delete old functions rather than keeping them alongside new ones
- Rename freely — symbols, modules, CLI flags, DSL keywords
- Never create wrapper functions, re-exports, or compatibility aliases
- Update all callers in the same commit as the breaking change
- Communicate breaking changes via
CHANGELOG.md(### Changed/### Removed) and GitHub issue comments — that is sufficient notice
The test suite is the safety net. If all tests pass after a rename, the rename is complete.
Consequences¶
Positive¶
- Codebase stays clean — no
_deprecated,_v1, or_compatsymbols accumulating - Refactors are atomic — the codebase is never in a half-migrated state
- Contributors read one canonical path, not two
- Evolution speed is unconstrained by compatibility obligations
Negative¶
- Branches that diverge from main for more than a day may require manual reconciliation
- External tooling (IDE plugins, scripts) that call Dazzle APIs directly will break silently on upgrade — acceptable given the current user base
Neutral¶
- CHANGELOG becomes the authoritative record of interface changes
- This policy is explicitly documented in
CLAUDE.mdso contributors don't introduce compatibility shims
Alternatives Considered¶
1. Deprecation Warnings¶
Mark old symbols with @deprecated and remove them after one release cycle.
Rejected: Adds dead code to every release. The single primary user doesn't need a release cycle of warning — a CHANGELOG entry plus a GitHub comment is enough.
2. Gradual Migration¶
Keep old and new APIs in parallel, migrate callers over multiple commits.
Rejected: Leaves the codebase in an inconsistent state between commits. Increases cognitive load — contributors must learn which API is current.
3. Compatibility Layers¶
Add thin wrappers that forward old call sites to new implementations.
Rejected: Wrappers accumulate. They become permanent because removing them is itself a breaking change. They hide the true shape of the API.
Implementation¶
This policy is enforced by convention, not tooling. Code review checks that no new _compat, _v1, or _deprecated symbols are introduced, and that breaking commits include a corresponding CHANGELOG.md entry.