If you've ever moved contacts from one app to another, you know the pain. One spreadsheet calls it "Phone," another "Mobile No.," a third "Contact Number." One has "Full Name," another splits it into "First" and "Last." Every tool exports data a little differently.
I built a CSV importer that solves this for a sales CRM (a system businesses use to track their customers and leads). You upload a spreadsheet from anywhere, whether it's Facebook Lead Ads, Google Ads, another CRM, or something typed up by hand, and it figures out which column is which. You can try it live.
I built it as a take-home assignment for a job interview in January 2026, but the problem turned out to be interesting enough that I wanted to write about how I approached it.
Why not just match column names?
The traditional approach is a lookup table: "if the column is called Phone or Mobile, put it in the phone field." It works until someone's spreadsheet says WhatsApp line or Tel (office). You end up writing endless rules and still missing cases.
A person, on the other hand, would glance at the column, see a bunch of numbers starting with +234, and say "that's obviously a phone number." That kind of common-sense reading is exactly what modern AI models are good at. So I let an AI model (Google's Gemini) do that part.
The AI is a smart assistant, not the boss
Here's the most important design decision in the whole project.
AI is great at understanding messy input, but it can occasionally be confidently wrong. So I split the work in two, like an office with an assistant and a manager:
- The assistant (the AI) only does one thing: read each row and say "this value looks like the email, this looks like the name, this looks like the phone number."
- The manager (plain, predictable code) makes every final decision about what's valid. For example:
- A contact with no email and no phone number is skipped, because a sales team can't reach them.
- Fields like "lead status" must be one of a fixed set of allowed values. If the AI suggests something else, it's rejected.
- Dates must be real, readable dates.
- Stray line breaks are cleaned out of text.
The AI never decides whether a record is good enough to import. That rule lives in ordinary code that behaves the same way every single time. This removes a whole category of "the AI had a bad day" problems.
I also asked the AI to reply in a strict, pre-defined format, so it can't hand back a jumbled answer that breaks the next step.
Working in small batches
A spreadsheet can have thousands of rows. Sending them all to the AI in one go would be slow, and a single hiccup could ruin the whole import.
So the importer works like a team sorting mail. It splits the rows into small bundles of about 20, and handles a couple of bundles at the same time. If a bundle fails, it tries again (up to twice, with a short pause between attempts). And if a bundle still fails after that, only those 20 rows are marked as skipped. The rest of your import carries on. One bad bundle never takes everything else down with it.
Showing progress as it happens
Nobody enjoys staring at a spinning wheel with no idea how long it'll take.
Instead of waiting until everything is finished and sending one giant answer, the server sends results back as each bundle finishes. On screen, you see a progress bar move and a results table fill in row by row. When it's done, you get a summary: how many were imported, how many were skipped, and why.
The flow is simple: upload your file, preview what's in it, confirm, and see the result.
One source of truth
A small but useful detail: the list of CRM fields and allowed values is written down in exactly one place, and every part of the app reads from it. The instructions sent to the AI, the checks that run afterwards, and the screen that shows you the results all use the same list. If the CRM ever adds a new field, it only has to change in one spot, and nothing drifts out of sync.
The takeaway
The lesson I'd pass on to anyone adding AI to a product: let AI do what it's good at, and keep firm rules for everything that must be right. Here, AI handles the messy, human job of understanding what a column means. Ordinary code handles the strict job of deciding what's acceptable. Together, they're more reliable than either would be alone.