vishal patel
← Case studies
Tech lead · Contentstack platform

Stopping unintended locale publishing: cross-locale release items

Enterprise customers were publishing content into markets it was never localised for, via master-locale fallback. I led the design that enforces the rule in the release service without breaking a single existing client.

api-designbackward-compatibilitymulti-localereleasesnode.jsmongodb
Rule enforced for every client (UI, API, SDK, CLI) — not just the UI
Zero new statuses, fields or error codes — existing integrations unaffected
Clear, different failure semantics for sync add, bulk add and deploy

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

diagram

Decision

Enforce in the v2 release service, with deliberately different semantics per entry point:

Entry pointBehaviourWhy
Synchronous add itemsAll-or-none; error lists the offending entry and localeThe user is waiting and can fix the input now
Bulk addSkip invalid items, add the restLarge batches shouldn't fail for one bad item
DeploySkip items that are no longer valid, marked SKIPPEDSafety net for items added before the setting was enabled
diagram

Backward-compatibility boundary:

  • Reused the existing SKIPPED status 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.