We were migrating customer data from three legacy systems into a new CRM, and the combined export was, professionally speaking, a crime scene. Forty-seven columns, eighty thousand rows, touched by many hands over many years, none of whom had ever agreed on a single convention. Phone numbers came in six distinct formats. Dates were sometimes YYYY-MM-DD, sometimes MM/DD/YY, and on several memorable occasions the literal string 'unknown.' One column contained the values Y, Yes, yes, TRUE, 1, and 0, all apparently meaning the same thing, which is a special and personal kind of cruelty to inflict on the next person who opens the file. That next person was me.
I had budgeted three days for the cleanup and quietly, privately assumed it would actually take five. The migration window was a single Saturday, and I really did not want to spend that Saturday discovering a seventh phone-number format at 2am with cold pizza and a dropping will to live.
Profiling before touching anything
I pasted the header row and the first twenty data rows into the Profile and Clean a Messy CSV with Reproducible pandas Code prompt, described the dataset honestly, and set the intended use to 'import into Postgres and use for email segmentation.' That last detail quietly steered every recommendation that followed — because it knew email validity actually mattered for the segmentation, and that strict, predictable typing was non-negotiable for a clean Postgres import. GPT 5.2 returned a column-by-column quality profile that found real issues in 31 of the 47 columns and rated each by severity, so for the first time I knew exactly where to spend my limited attention instead of fixing the easy columns first because they were easy.
Why the ordering of the plan saved me
The cleaning plan came back with fourteen numbered steps, and the ordering of those steps was the entire point. Standardize the booleans first, THEN normalize the phone numbers, THEN parse the dates, THEN handle the nulls — because several later steps depended on a column already being a consistent, predictable type. The first time I ever tried to clean a file like this, years ago and on my own, I did null handling first and then watched half my type coercions fail on the placeholder values I had just lovingly filled in. The plan's stubborn insistence on order is the precise difference between a single clean run and an afternoon-long debugging spiral where every fix breaks the previous one.
- Fourteen separate, commented functions — one per step — so I could test and tweak each one in isolation instead of debugging a single giant chained expression by squinting.
- A printed before/after summary showing the phone_number column going from 22 percent null down to 4 percent after normalization, so I could see the cleaning actually working.
- Two honest TODO stubs for the decisions only a human could make: what to impute for a missing customer_since date, and whether to drop or merely flag the 340 rows with invalid email formats.
- Idempotency by design, so re-running it on the inevitable second export two weeks later was a calm no-op rerun rather than a fresh re-debugging session.
The part that paid for itself twice
Because the script logged every single row removal with a reason and a running count instead of silently dropping data into the void, I could hand the migration log straight to our compliance reviewer and prove exactly what we excluded and precisely why. That was not even my goal — I genuinely just wanted clean data and my Saturday back — but 'we dropped 340 rows, here is the categorized reason for each batch' turned what is normally a tense, suspicious compliance review into a five-minute conversation that ended in a thumbs-up. The reproducibility meant the second export, which arrived exactly as predicted because legacy systems are nothing if not predictable in their unreliability, ran through the identical script in under a minute.
The whole migration took one afternoon instead of the three-to-five days I had braced for. If you are staring down a CSV that three different systems took turns ruining over a decade, do not clean it by hand and do not write the script from scratch at midnight. Grab the Profile and Clean a Messy CSV with Reproducible pandas Code prompt on Prompt Dock, paste in your worst twenty rows, and let it write the ordered plan and the code. Once the data is finally clean and sitting in Postgres, my Write a SQL Query from a Plain-English Business Question prompt takes over from there and you never have to think about the six phone-number formats again.