TL;DR

When two systems share data (an ERP and a CMS, an admin app and a public site, a master DB and a cache), the cleanest contract is: one owner and one direction per field. The moment a field is editable in both, you’ve signed up for a conflict you’ll resolve forever.

This is the rule that turned a Sanity-on-top-of-NetSuite setup (case study) from “which value is right?” into something operators can actually trust.

The trap

When you pull ERP data into a CMS, the tempting move is to make everything editable. It feels generous. It immediately creates a conflict: if a marketer edits a weight in the CMS and the ERP later re-syncs, whose value wins? Same question for descriptions, categories, SEO, pricing — every shared field. You end up writing a merge policy, and the merge policy is where bugs live.

The rule

For every field, decide two things and write them down:

  1. Who owns it — the system of record.
  2. Which direction it syncs — owner → consumer, never both ways.

Then enforce the rule in the schema, not in a wiki:

  • Operational/commercial data (item code, class, weights, dimensions, pallet layout, barcodes, pricing) — ERP owns it. Syncs one-way into the CMS. Read-only in the CMS UI, written only by the sync process via an API token.
  • Marketing/presentation data (descriptions, imagery, recipes, SEO, cross-links) — CMS owns it. ERP never touches it.

In the CMS schema this means two visibly different sections: operational fields grouped into a “locked” section (read-only in the UI), and marketing fields into an “editable” section listed first. A living field-ownership map documents every field, its source, and its direction.

Why this works

  • No “which value is right?” debates — the schema answers it before anyone asks.
  • Editors get a clean, obvious surface — “this part is from the ERP, this part is yours.”
  • The sync can run safely without ever clobbering human work, because it only writes into fields it owns.
  • The next engineer onboards in an afternoon, because the rule is the schema.

How to apply it elsewhere

  • Same as draft/publish in a CMS: data is owned by the editor, publish status is owned by the publish action; the published copy is read-only everywhere except the publish step.
  • Same as app/cache: app owns writes, cache is read-only and rebuilt; never edit the cache directly.
  • Same as frontend/backend for derived values: the backend owns the canonical number, the frontend renders it but never persists its own version.

If you notice yourself writing a merge policy, stop and ask: can I just pick an owner?

When not to use it

  • Truly collaborative fields where both systems contribute new facts (e.g. one writes “submitted”, the other writes “approved”). Solution: split them into two fields, each with one owner. Don’t share one field.
  • Migration windows where you genuinely need to write from both sides briefly. Treat it as temporary, write the cutover date down, and finish it.

The one-line version

Don’t sync everything. Decide who owns each field.