Almost every product that holds data has a moment where somebody arrives with data they already have. A spreadsheet, an export from the tool they’re leaving, a file somebody’s assistant assembled by hand. That import is usually the first serious thing they do with the software, and it happens before they trust it — which means whatever it does is not just a feature working or failing, it’s the evidence they use to decide what kind of software this is.

The trouble is that importers get built against clean data. Someone writes a sample file, the parser handles it, the tests pass, and the feature ships. Real files are not that. They have merged cells and trailing whitespace and a column that’s mostly dates with four entries that say “ASAP.” They have duplicate rows because two people maintained the sheet. They have a header row that isn’t the first row. None of this is unusual; it’s what data looks like after living in an office for three years, and an importer that only accepts the clean version is an importer that works for nobody.

Which makes the interesting design question not “can we parse it” but what happens when we can’t. The worst answer is failing the whole file on row four hundred, because the person then has to guess what was wrong, fix it blind, and try again — and each attempt costs them the full upload. Nearly as bad is silently skipping the rows that didn’t work, because now the data is in and it’s incomplete and nobody knows which part. What people actually need is the boring middle: tell me what you couldn’t take, tell me which rows and why in language about my data rather than yours, and let me fix those without redoing the rest.

That “in language about my data” part carries more weight than it looks. ValidationError: field 'created_at' expected ISO-8601 describes the program’s disappointment. “Row 412: the date ‘last spring’ isn’t a date we can read” describes the customer’s problem, and it’s the difference between a fixable file and a support ticket. The importer knows the row number, the column heading as it appeared in their file, and the value — using all three costs almost nothing and changes who can solve the problem.

There’s also a question of what the import commits to. Halfway through a large file is a genuinely bad place to stop, and a partially-imported dataset is worse than a rejected one because it looks finished. Either the whole thing lands or none of it does, or — more practically at real sizes — it lands in units the customer can reason about, with a clear statement of what got in and the ability to run the rest again without duplicating the part that worked. That last property is the retries argument in yet another costume: an import someone might have to repeat should be safe to repeat.

None of this is glamorous work, and it tends to be scoped as a one-time onboarding chore. But it’s the point where a new customer discovers whether the product treats their reality as an edge case, and the first hour of that relationship buys more than most features do.