Context
In a headless CMS, an entry that isn't localised in a locale falls back to the master locale's content. That's convenient for authors, but for enterprises with regulated or market-specific content, a release deployed to fr-fr could publish untranslated master content to French users. The Disable Fallback Publishing setting had to guarantee that entries are only released in locales they're actually localised in.
Constraints
- Releases are used from the UI, the Management API, SDKs and the CLI. A UI-only check would be bypassed.
- There were two generations of release APIs (v1 and v2) with live customers on both.
- Customers had existing integrations parsing release item statuses and error payloads.
- It had to work at enterprise content volumes, where bulk operations add thousands of items.
Options considered
Decision
Enforce in the v2 release service, with deliberately different semantics per entry point:
| Entry point | Behaviour | Why |
|---|---|---|
| Synchronous add items | All-or-none; error lists the offending entry and locale | The user is waiting and can fix the input now |
| Bulk add | Skip invalid items, add the rest | Large batches shouldn't fail for one bad item |
| Deploy | Skip items that are no longer valid, marked SKIPPED | Safety net for items added before the setting was enabled |
Backward-compatibility boundary:
- Reused the existing
SKIPPEDstatus and error envelope. No new fields, statuses or error codes, so no client had to change parsing. - Legacy v1 releases were left unchanged. The new rule applies only in v2, and that boundary was documented explicitly.
- Deploy-side changes went through review with the team that owns the delivery service.
Outcome
- The rule is enforced for every client path, not just the UI.
- Existing integrations kept working with no contract changes.
- Users get actionable errors (which entry, which locale) instead of silent fallback.
Key takeaways
Choosing where to enforce a rule (the service, not the UI), choosing different failure semantics per use case, and treating "no new error codes" as a design goal. See also: Idempotency, API Evolution.