Who May Write This Field? A Merge Table for Two Enrichment Services
Two background services enrich the same customer or lead record. One cleans addresses. The other classifies the business type. Both read the record, think for a while, and write it back. If each writes the whole record, the slower one silently erases the faster one's work. The remedy proposed here is to decide, per field, who may write, and to make every write conditional. It is a design proposal, not a description of any existing system.
Primary artifact: the field ownership merge table
List every field on the record with these columns:
- Field
- Owner (one service, a human, or shared with a precedence rule)
- Who may propose a change
- Write condition
- Conflict outcome
Decision branches
- Single owner: only that service writes the field. Another service that wants a different value files a suggestion elsewhere and does not write.
- Shared with precedence: both may write, and a written precedence order decides. For example, a human edit beats a service, and a newer source timestamp beats an older one from the same tier.
- Human-locked: once staff edit the field, services may only propose, never overwrite.
Writes go through a patch of named fields plus a version check. The writer sends the record version it read. If the stored version moved, the write is rejected and the service re-reads and recomputes. Retrying a rejected conditional write is safe because it was rejected before any change. If a write result is uncertain, such as a timeout, read the record and compare before any further write.
Worked hypothetical example
A lead record has six fields: street, city, postcode, business category, website status, internal note.
| Field | Owner |
|---|---|
| street | address service |
| city | address service |
| postcode | address service |
| business category | classifier service |
| website status | shared, newer source timestamp wins |
| internal note | human only |
Count: 3 + 1 + 1 + 1 = 6 fields, with 3 single-service fields in the address group.
Both services read version 7. The classifier finishes first and writes category with expected version 7, so the record becomes version 8. The address service then writes its three fields with expected version 7. Under whole-record overwrite it would erase the category. Under this design the write is rejected for the stale version. The address service re-reads version 8, sees that the category is not its field, reapplies only street, city and postcode, and writes with expected version 8. The result is version 9, containing both contributions.
Now suppose a staff member edits the city between versions. The city is human-locked from that moment, so the address service's recomputed city is stored as a suggestion, not written. Suggestions to review: 1. Fields overwritten silently: 0.
Stop conditions
Stop the pipeline and review by hand if:
- A field has two owners and no precedence rule.
- The same record is rejected for a stale version repeatedly. That suggests a loop or a service writing too eagerly.
- A service needs to write a field it does not own. Change the table, deliberately, before changing the code.
Proposed acceptance tests (not yet performed)
- Run the two services concurrently against one record many times. The final record must contain both services' fields every time.
- Force a stale version write. It must be rejected with no partial change.
- Edit a locked field as a human, then run the owning service. The field must stay unchanged and a suggestion must appear.
- Attempt a write to a field the service does not own. It must be refused.
- Review the table and confirm that every field has exactly one owner or one precedence rule.
The table is the contract. Code that disagrees with it is the defect.