A CSV import needs an identity rule before it needs an Update button
An import preview can tell you that a row is valid CSV without telling you which application record it represents. That distinction becomes visible when two customers share a name, an external identifier has leading zeros, or the same identifier appears twice in one file. This is a proposed approach for a hypothetical admin import. It builds on the preview-and-commit boundary: before showing changes, decide how a source row identifies a record. A position in the file is not a business identity Suppose the file contains: external_id,display_name 00123,Acme 00456,Acme 00123,Acme Limited The row number tells you where the source value came from. The display name describes the customer. The external identifier may provide the matching key, if the product's contract says it does. I would keep those concepts separate in the preview. “Row 4 updates customer 00123” communicates more than “Acme will be updated.” It also makes an error actionable: the user can find the o
An import preview can tell you that a row is valid CSV without telling you which application record it represents.
That distinction becomes visible when two customers share a name, an external identifier has leading zeros, or the same identifier appears twice in one file.
This is a proposed approach for a hypothetical admin import. It builds on the preview-and-commit boundary: before showing changes, decide how a source row identifies a record.
A position in the file is not a business identity
Suppose the file contains:
external_id,display_name
00123,Acme
00456,Acme
00123,Acme Limited
The row number tells you where the source value came from. The display name describes the customer. The external identifier may provide the matching key, if the product's contract says it does.
I would keep those concepts separate in the preview. “Row 4 updates customer 00123” communicates more than “Acme will be updated.”
It also makes an error actionable: the user can find the original row without relying on the preview's sort order.
Choose the matching key explicitly
Possible rules include a unique source identifier, an approved composite key, or a user-selected mapping.
None is automatically correct for every product. An external identifier may only be unique within one source system. An email address may change. A display name may be shared by several records.
I would describe the chosen scope beside the matching option. If identity is (source system, external identifier), dropping the source-system part during matching can merge unrelated records.
Preserve meaningful source values
Converting 00123 to the number 123 may be appropriate for a quantity. It can be wrong for an identifier whose leading zeros matter.
Normalization should follow the field's domain. Trimming whitespace, changing case, parsing dates, or coercing numbers are decisions with consequences.
For an important field, I would make the normalized interpretation inspectable and retain enough source context to explain it. A preview that silently transforms values makes debugging the import harder.
Separate zero, one, and many matches
I would give each source row a matching outcome before proposing its mutation:
| Outcome | Possible policy |
|---|---|
| No record matches | Propose creation, if permitted |
| One record matches | Show the proposed update |
| Several records match | Require review or reject the ambiguous row |
Those are examples of policies. The useful part is that ambiguity remains visible. Picking the first match because it makes the code simpler is a hidden data decision.
Duplicates inside the file need a rule too
The example file repeats 00123. Should the last row win, should the rows be merged, or should the file be rejected?
Any of those can be a deliberate product rule. I would avoid letting parser iteration order make the choice accidentally.
Show the affected source rows and explain the result before the user commits. If a duplicate is resolved automatically, make the resolution discoverable.
The rule also needs to survive a retry. Reapplying the same file should follow the same identity contract; it should not alternate between creation and update because the first attempt changed the matching state.
Verify matching assumptions at commit
A preview can become stale. Another user may create a record with the same identifier or change a matching field after the plan was generated.
The commit needs to enforce the chosen policy against the current data. Unique constraints and atomic guarded updates can help implement parts of that policy, but the user still needs to understand the result when a conflict occurs.
Start with a deliberately awkward file
My test file would include repeated identifiers, repeated names, leading zeros, blank identifiers, and records from different source systems.
For each row, the preview should answer: what matched, why it matched, what will change, and which ambiguity needs attention.
That makes the Update button the final step in an understandable decision, rather than the place where an unexplained matching rule becomes permanent.