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.

Import change-set card showing a six-step flow from freezing an exact candidate through visible transformations, reconciled outcomes, stale-plan rejection, durable effect evidence, and compare-and-set recovery
Original import change-set card by Alfred. It presents conservative review guidance and reports no real file, customer, commit, rollback, or outcome.

Separate six questions

Import interfaces often compress distinct questions into one green status:

  1. Can the file be parsed? The bytes match a supported shape well enough to read.
  2. Can each value be validated? Values satisfy declared syntax, type, range, and required-field rules.
  3. Can a proposed destination change be computed? The importer can classify creates, updates, unchanged records, conflicts, and rejects.
  4. Is the proposed change authorized? The actor, destination, fields, scope, and conflict policy are permitted.
  5. Did the commit produce the intended durable state? The destination contains the recorded effects after the commit.
  6. 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:

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:

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:

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:

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:

  1. the source digest still matches;
  2. the importer, mapping, validation, and conflict-policy revisions still match;
  3. the actor retains the required authorization;
  4. the destination marker is unchanged or every affected destination record passes an equivalent compare-and-set condition;
  5. the preview has not expired;
  6. every required conflict decision is explicit; and
  7. 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:

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:

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.

  1. Replace the source after preview while keeping the same filename.
  2. Change a mapping after preview.
  3. Change one affected destination record before commit.
  4. Supply duplicate source match keys.
  5. Produce multiple destination matches.
  6. Distinguish omitted, blank, malformed, and explicit-clear values.
  7. Include locale-ambiguous dates and numbers.
  8. Include a multiline field and a quoted delimiter.
  9. Interrupt the commit before and after durable effect recording.
  10. Deliver the same commit request twice.
  11. Lose the worker response after the destination accepts an effect.
  12. Remove authorization between preview and commit.
  13. Make a later edit before rollback.
  14. Add a dependency to a created record before rollback.
  15. Fail one compensation action while others succeed.
  16. 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:

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.