docs(architecture): ADR 0003 — API design over backwards compatibility until 1.0 - #176
Merged
Merged
Conversation
…y until 1.0 Records the rule that has been decided case-by-case until now, and the corollary that gives it teeth. Two phases: until 1.0, a cleaner surface justifies a break, with no shims or transitional aliases; from 1.0, evolvability becomes a design criterion in its own right. The corollary is what links them. Installing an evolvability mechanism is itself a breaking change — `#[non_exhaustive]` blocks struct literals and `..base` functional updates alike — so the hinges can only be fitted while breaking is still free. Phase 2 is affordable only if phase 1 is spent installing them. Context for the decision: cargo-semver-checks flagged two additive changes on #174 (`constructible_struct_adds_field`, `enum_variant_added`), which creates standing pressure to contort the design to stay compatible. In the absence of a rule that pressure wins by default, because preservation is the option that requires no work. That default is how pagination ended up declared in four config structs, two of which can silently diverge. Also replaces the stale "none written yet" line in the ADR README with an index, and reserves 0002 for T-66TG per the numbering convention.
Deploying ontogen with
|
| Latest commit: |
67749f8
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://ac1401f3.ontogen.pages.dev |
| Branch Preview URL: | https://docs-adr-0003-api-design-ove.ontogen.pages.dev |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Writes down a rule that has been decided case-by-case until now, and the corollary that gives it teeth.
The rule
Until 1.0 — API design trumps backwards compatibility. A cleaner surface justifies a break. No shims, no transitional aliases, no
foo2()besidefoo().From 1.0 — the ability to evolve the API is itself part of the API. A surface that cannot absorb a new field, variant or mode without a major bump is not finished, however clean it looks.
Why they are one decision and not two
Installing an evolvability mechanism is itself a breaking change. Per the Rust reference, a
#[non_exhaustive]type "cannot be constructed with a StructExpression (including with functional update syntax)" — so adding it breaks every consumer literal and every..baseupdate.It can therefore only be fitted while breaking is free. Phase 2 is affordable only if phase 1 is spent installing the hinges. The pre-1.0 window is not just for getting the shape right; it is for fitting the joints the shape will later need to bend at.
What forced the question
Two forces had been pulling against each other with nothing to arbitrate:
constructible_struct_adds_fieldforEntityDef.doc/FieldDef.doc,enum_variant_addedforCodegenError::Docs. Every such flag creates quiet pressure to contort the design to keep the check green.ServersConfigandClientsConfigduplicate twelve fields, and both stages independently scan their own copy ofapi_dir. Pagination is one instance of a bug class with twelve.With no rule, the decision defaults to whichever is locally cheaper — always preservation, since it requires no work. That default is how the divergence accreted. Nobody chose it; it is the residue of never having chosen.
Notable contents
mode, the nestedlistcapability object, reservedorder_by/orderslots), which is the evidence the instinct was sound.semver_check = trueexplicitly stays. The rule is "do not avoid breaks," not "do not detect them" — the check drives the version bump rather than a redesign. Dropping it is listed as a rejected alternative because it is the tempting misreading.Also
Replaces the stale "None written yet" line in the ADR README with an index, and records 0002 as reserved by T-66TG per the numbering convention.
Review focus
The decision itself, not the prose. Two calls made without explicit sign-off, both easy to reverse: extending the rule to the generated wire protocol (with the asymmetry called out rather than a separate policy), and leaving the sweep untracked pending a decision on where it should live.