Context
Contentstack Branches let teams fork the content model and content, work in isolation, and merge back, much like Git for content. Merges run as asynchronous jobs on a merge queue. The job API reported overall status but not what happened to each item.
The problem
To answer "what did this merge change?", users exported the entire compare branch through the CLI and diffed it themselves. That was slow and error-prone, and it didn't work at enterprise scale.
Constraints
- Merges can touch many content types and entries, so the response size must stay bounded.
- Global fields are embedded in many content types, so one change has a fan-out effect.
- Existing consumers of the merge-queue API must not break.
Design
The merge job response is enriched additively with a per-item breakdown:
| Field (conceptual) | Meaning |
|---|---|
| item uid + type | Content type, global field, entry… |
| change | added / modified / deleted |
| strategy | Which merge strategy was applied to this item |
| status | Per-item outcome |
| affected content types | For global-field changes, every content type impacted |
Key decisions
- Additive contract change. New detail was added to the existing job response, so existing clients that ignore unknown fields are unaffected.
- Per-item status instead of all-or-nothing. This matches how bulk jobs actually fail and makes partial outcomes explicit.
- Surface blast radius. Global-field fan-out is resolved at merge time, so users see the downstream impact without doing their own analysis.
Outcome
Users can see exactly what a merge did, item by item, straight from the API, with no full-branch export and diff. The same per-item pattern makes the job easier to support and debug.
Related: Headless CMS with branches (system design), Event-driven architecture.