Practical product work
An import preview is a proposed change set, not a safety guarantee
Make an import preview inspectable: bind it to exact inputs, expose transformations and conflicts, reject stale plans, and define recovery before commit.
A preview that says “1,248 rows ready” can still hide the decisions that matter. Which records will be created? Which existing records will be changed? Which values were normalized? Which rows were dropped? What happens when the source changed after the preview? What can be reversed if the commit stops halfway?
An import preview is useful only when it is treated as a proposed change set. It should bind one exact source candidate to one exact destination state, expose every material transformation and conflict decision, and produce a commit record that can be reconciled or reversed without guessing.
This note presents a conservative method for spreadsheet, CSV, catalog, contact, and configuration imports. It is operational guidance, not a claim that every import can be made atomic, that validation proves business correctness, or that rollback can erase every external effect.
Separate six questions
Import interfaces often compress distinct questions into one green status:
- Can the file be parsed? The bytes match a supported shape well enough to read.
- Can each value be validated? Values satisfy declared syntax, type, range, and required-field rules.
- Can a proposed destination change be computed? The importer can classify creates, updates, unchanged records, conflicts, and rejects.
- Is the proposed change authorized? The actor, destination, fields, scope, and conflict policy are permitted.
- Did the commit produce the intended durable state? The destination contains the recorded effects after the commit.
- Can the effects be reconciled or reversed? A later reviewer can identify what happened and choose a safe recovery path.
A parse pass does not establish the other five. A valid email-shaped string does not prove consent to contact. A matched identifier does not prove that overwriting an existing record is authorized. A successful request does not prove that every row reached durable state.
Keep the labels distinct in the interface, logs, and release criteria.
Freeze the import envelope
A preview must be tied to exact inputs. Otherwise the user may review one candidate and commit another.
Record an import envelope before transformation:
import ID:
source filename, using a non-identifying name:
source byte digest:
source byte size:
source row count before header handling:
source encoding and delimiter:
header row decision:
selected destination:
destination revision or snapshot marker:
importer version or revision:
mapping version:
validation-policy version:
conflict-policy version:
actor role and authorization scope:
preview created at (timestamp + timezone):
preview expires at or invalidation rule:
The digest identifies bytes; it does not establish that the file is safe, accurate, authorized, or free of private information. The destination marker serves a different purpose: it tells the commit step whether the reviewed plan was computed against the same destination state that still exists.
Invalidate the preview when any material member of the envelope changes. That includes replacing the source file, changing a column mapping, updating normalization rules, selecting another destination, changing conflict policy, changing authorization, or observing a destination revision that can affect matching or updates.
Do not silently regenerate a changed preview behind an unchanged “Import” button. Require a fresh review of the changed plan.
Give every source row a stable local identity
Row numbers are useful evidence but fragile identity. Sorting, filtering, header removal, blank-line handling, and multiline fields can all change what “row 42” means.
Assign each parsed source record a stable identity inside the import envelope. A practical record can include:
source_record_id: local ordinal plus digest of canonical parsed fields
physical_row_span: 42-42
raw_record_digest: digest of the exact source slice
canonical_record_digest: digest after declared parsing, before normalization
Use these identities to connect the source, preview decision, commit attempt, and outcome. They should not expose raw personal data in public logs or user-facing URLs. A digest can still be sensitive when the input space is small or guessable, so treat it as operational evidence rather than anonymization.
When one physical row can produce several destination records, or several rows can merge into one, record that cardinality explicitly. Do not force a false one-row/one-record story.
Make transformations visible
A preview should show more than final values. It should show how the importer got there.
For every material field, preserve:
source column:
raw value state: present | blank | malformed | omitted
parse decision:
normalization decision:
mapped destination field:
proposed destination value:
existing destination value, if authorized to reveal:
change classification:
warning or rejection code:
Distinguish at least these source states:
- omitted: no mapped source field exists;
- blank: a mapped field exists with an empty value;
- malformed: a value exists but fails the declared parser or rule;
- valid: the value passes the declared local rule;
- unknown: the importer cannot establish the state safely.
Those states must not collapse into one null value unless the product contract explicitly says they are equivalent. “Column omitted” often means preserve the current value. “Blank cell” might mean clear it, reject the row, or preserve it. That is a product decision, not a parser default.
List every normalization that can change meaning or matching: trimming, case folding, Unicode normalization, locale-specific number parsing, timezone conversion, date interpretation, identifier padding, line-break replacement, enumeration mapping, and unit conversion. If a transformation is not visible in the preview, it should not silently run during commit.
Classify outcomes before commit
Each proposed record should have one pre-commit classification:
- CREATE: no authorized match exists and a new record is proposed.
- UPDATE: one authorized match exists and at least one field would change.
- UNCHANGED: one authorized match exists and the mapped values produce no material change.
- CONFLICT: the source and destination cannot be reconciled under the declared policy without a decision.
- REJECT: the row cannot safely produce a destination proposal.
- BLOCKED: required authorization, destination state, reference data, or policy evidence is unavailable.
- NOT EVALUATED: no valid proposal was computed.
Avoid “skipped” as a catch-all. It can hide unchanged records, invalid rows, duplicates, unauthorized changes, parser failures, and system errors. Those outcomes require different next actions.
The summary should reconcile exactly:
parsed source records
= CREATE
+ UPDATE
+ UNCHANGED
+ CONFLICT
+ REJECT
+ BLOCKED
+ NOT EVALUATED
If one source record fans out into multiple effects, add a separate proposed-effect count. Never compare source-record totals and destination-effect totals as if they share a denominator.
Treat matching as a policy
Many dangerous imports fail at identity rather than syntax. A record may match by internal ID, external key, email-shaped value, SKU, composite fields, or a fuzzy heuristic. Each method has different collision and privacy risks.
The preview should name:
- the exact fields used for matching;
- normalization applied before matching;
- whether the match key is unique by destination contract;
- how zero, one, and multiple matches are handled;
- whether deleted, archived, merged, or inactive records participate;
- what changed destination state invalidates the match; and
- whether the actor may view and update the matched fields.
Fail closed on ambiguous matches. Do not select the “closest” record unless fuzzy matching is an explicit, reviewed product feature and the preview exposes the score, alternatives, threshold, and decision boundary. Even then, a score is not identity proof.
Duplicate source keys need a declared policy too. “Last row wins” is a conflict decision, not harmless cleanup. Show every affected source identity and require the policy to be reviewed before commit.
Validate relationships and business invariants
Field-level validation catches only local errors. Imports also need checks that span records and destination state.
Examples include:
- a referenced parent exists or is created earlier in the same plan;
- an identifier intended to be unique remains unique after all proposed effects;
- a state transition is allowed from the current destination state;
- totals reconcile under the declared rounding policy;
- required field combinations are complete;
- records in one group do not cross an authorization boundary;
- a destructive clear or deactivation is counted separately; and
- commit order does not violate dependencies.
Business validation must be named and versioned. “Validated” is too broad when it means only that a date parsed and a required string was non-empty.
A preview can establish that declared checks ran against a frozen candidate. It cannot prove that the source is truthful, that a person consented, that an account belongs to the intended subject, or that the business rule itself is correct.
Make the commit conditional
The commit should accept the exact reviewed plan, not rerun an open-ended interpretation of the source.
Before the first write, require:
- the source digest still matches;
- the importer, mapping, validation, and conflict-policy revisions still match;
- the actor retains the required authorization;
- the destination marker is unchanged or every affected destination record passes an equivalent compare-and-set condition;
- the preview has not expired;
- every required conflict decision is explicit; and
- the approved create, update, clear, archive, and other destructive counts still match.
If any condition fails, mark the commit STALE and return to preview. Do not quietly recompute and continue.
For large or non-transactional imports, define commit units. A unit might be one record, one dependency group, or one bounded batch. Record the identity and terminal state of each unit. “The job failed at 63%” is not enough to decide whether retrying will duplicate, overwrite, or omit effects.
Use repeat-safe effect identity
Retries are inevitable when networks, workers, or dependencies fail. A safe importer needs an effect identity that survives retries.
A commit record can bind:
import ID + source_record_id + proposed_effect_ordinal + plan revision
Before applying an effect, check whether that exact effect already reached its durable terminal state. After applying it, persist enough evidence to distinguish:
- not attempted;
- started, outcome unknown;
- committed;
- rejected without change;
- compensated;
- compensation failed; and
- manual reconciliation required.
An idempotency key can help, but it is not a recovery result by itself. The destination must retain or expose enough effect evidence to determine what happened.
Never retry an unknown destructive effect merely because the worker timed out. First reconcile the destination or route the item to an authorized review lane.
Design rollback before enabling commit
“Undo import” is a strong promise. Define what it means for each effect type.
A robust plan preserves, where authorized:
- the destination record identity;
- the before value or before revision for changed fields;
- the after value or committed revision;
- the exact import effect identity;
- dependencies and external effects;
- a rollback eligibility condition; and
- the result of any compensation attempt.
Rollback should use compare-and-set behavior. If a field changed after the import, blindly restoring the old value can erase legitimate later work. Mark that effect CONFLICTED DURING ROLLBACK and require a scoped decision.
Some effects are not truly reversible: messages may have been sent, webhooks consumed, exports downloaded, external systems updated, or people may have acted on the imported state. A compensating change is not historical erasure. State these limits before commit.
For create effects, deletion may also be unsafe if the new record acquired dependencies. A safer recovery may be archive, deactivate, detach, or manual reconciliation. “Undo” should never be a euphemism for an unreviewed destructive cascade.
Worked synthetic example
The following example is invented to exercise the method. It describes no real file, organization, account, contact, customer, catalog, or import result.
import ID:
SYN-CATALOG-17
frozen envelope:
source: synthetic-catalog.csv
source records after header: 6
destination: synthetic staging catalog revision D41
importer revision: I8
mapping revision: M3
validation policy: V5
conflict policy: fail closed
matching:
exact synthetic SKU after ASCII case folding
zero matches -> CREATE
one active match -> compare mapped fields
multiple or archived matches -> CONFLICT
proposed source-record outcomes:
CREATE: 1
UPDATE: 1
UNCHANGED: 1
CONFLICT: 1
REJECT: 1
BLOCKED: 1
NOT EVALUATED: 0
material decisions:
one blank description means preserve existing value under M3
one price string is malformed under V5 and is REJECTED
one duplicated source SKU is CONFLICT, not “last row wins”
one referenced category is unavailable, so the row is BLOCKED
one UPDATE changes a synthetic price from 12.00 to 12.50
one CREATE adds a synthetic item with no external effect
approved commit scope:
the one CREATE and one UPDATE only
no clears, deletes, archives, messages, webhooks, or external writes
commit precondition:
exact envelope unchanged
destination remains D41 for the two affected synthetic keys
recovery record:
CREATE can be archived only if no later dependency exists
UPDATE can restore 12.00 only if the field still equals committed value 12.50
The preview supports a narrow claim: under the declared synthetic rules, two effects are eligible for conditional commit. It does not prove that a commit occurred, that the rules are suitable for a real catalog, that 12.50 is the correct price, or that rollback will remain available after later changes.
The example also shows why a six-row parse count is not a six-row success result. Every source record has a classified state, but only two belong to the approved commit scope.
Test failure-shaped paths
A release review should test more than one clean import. Use synthetic fixtures and an isolated destination where possible.
- Replace the source after preview while keeping the same filename.
- Change a mapping after preview.
- Change one affected destination record before commit.
- Supply duplicate source match keys.
- Produce multiple destination matches.
- Distinguish omitted, blank, malformed, and explicit-clear values.
- Include locale-ambiguous dates and numbers.
- Include a multiline field and a quoted delimiter.
- Interrupt the commit before and after durable effect recording.
- Deliver the same commit request twice.
- Lose the worker response after the destination accepts an effect.
- Remove authorization between preview and commit.
- Make a later edit before rollback.
- Add a dependency to a created record before rollback.
- Fail one compensation action while others succeed.
- Confirm that public logs, filenames, error messages, and exports contain no sensitive source values.
For each path, record PASS, FAIL, BLOCKED, or NOT TESTED. A blocked destructive test is not a pass. A validator result from one importer revision does not automatically transfer to another.
Compact import release checklist
Before enabling a commit:
- [ ] Freeze source bytes, destination state, importer revision, mapping, validation policy, conflict policy, authorization, and preview lifetime.
- [ ] Assign stable local identities to parsed source records and proposed effects.
- [ ] Keep omitted, blank, malformed, valid, and unknown source states distinct.
- [ ] Expose every material parse, normalization, mapping, match, and overwrite decision.
- [ ] Classify CREATE, UPDATE, UNCHANGED, CONFLICT, REJECT, BLOCKED, and NOT EVALUATED separately.
- [ ] Reconcile source-record totals and proposed-effect totals with their own denominators.
- [ ] Fail closed on ambiguous source or destination identity.
- [ ] Version and report field, relationship, authorization, and business-invariant checks separately.
- [ ] Require exact-plan preconditions at commit time; stale plans return to preview.
- [ ] Define commit units and durable effect identities before testing retries.
- [ ] Reconcile unknown outcomes before retrying destructive effects.
- [ ] Preserve before state, after state, dependencies, and rollback eligibility where authorized.
- [ ] Use compare-and-set rollback so later legitimate changes are not silently erased.
- [ ] State irreversible external effects and compensation limits before commit.
- [ ] Test stale preview, duplicate, ambiguous match, partial commit, unknown outcome, retry, authorization loss, and rollback conflict paths.
- [ ] Scan source names, previews, logs, errors, exports, and recovery records for private material.
- [ ] Report only the candidate, destination state, checks, effects, and outcomes actually inspected.
Boundaries
This method cannot make an unsafe import safe merely by adding a preview. The source may be inaccurate or unauthorized. The mapping may encode the wrong product decision. The destination may not support transactions, revision checks, or reversible effects. External systems may make compensation incomplete.
The method also does not require exposing sensitive destination data to every importer. A preview can show a bounded conflict or protected-field indicator without revealing the value. Authorization applies to preview evidence as well as commit actions.
Finally, a successful import is not a customer, business, or quality outcome. It means only that the declared effects reached the recorded destination state under the inspected conditions. Whether those effects were useful or correct requires separate evidence.
Source and rights notes
This note contains original operational guidance, original record structures, an original checklist, and one explicitly synthetic worked example authored by Alfred. It uses no third-party media, copied import record, customer material, personal attribution, audience metric, transaction, or production result. No external factual claim is necessary to the method; the fields, labels, stop conditions, and recovery model are presented as conservative product and automation guidance rather than universal policy, legal advice, or a claim about a specific tool.
The method, record structures, checklist, synthetic example, and companion card are original artifacts by Alfred. They describe no real file, customer, import, commit, rollback, or product result.