Example build

Twenty years of customers, stuck in software you hate.

An illustration of what I build, not a client project. The data is usually the most valuable thing a small business owns, and fear of losing it is the single most common reason people stay on a system they complain about weekly.

If you asked me to do this

Who it fits
Anyone leaving legacy software, a spreadsheet estate, or a system being retired
Scale I have handled
Tens of thousands of customer records, with their history
Time
1–3 weeks, depending on how messy it is
Typical range
$600 – $3,000

Nothing is committed to the new system until you have read a report and said yes to it.

The problem

Nobody is afraid of the new software. They are afraid of the move.

The old system holds every customer, every past job and every price ever agreed. It also holds the same person entered four ways, records with no email, and duplicates created by two people on the same afternoon in 2011.

A bad migration is the one mistake you cannot undo by hand. That is why it deserves more care than the software it is feeding, and why the answer is never a single import button. It is also why the cheapest quote you get for this is usually the most expensive one you will pay.

How it actually runs

The same loop, until the report is boring.

01

Export

Whatever the old system will give up — CSV, a database dump, a printed report somebody has to retype. I have worked with all three.

02

Agree what "the same customer" means

Same email? Same phone? Same name at the same address? Same name only — which is how you end up merging two unrelated businesses. Half an hour on this decides the whole job.

03

Dry run

The import runs against a copy. Nothing is committed. Out comes a report: imported, merged, needs review, and the exact rows in each pile.

04

You read the report

You will find things in it that surprise you about your own data. That is normal, and it is the point of doing this before anything is live.

05

Repeat

Rules adjusted, dry run again, report again. As many times as it takes until nothing in the report is a surprise to anybody. Three rounds is normal; one round means somebody is not reading it.

06

Commit, with a way back

The real import runs. Rollback stays available until you sign it off, and the whole thing can be re-run from scratch rather than patched.

The import is written to be repeatable on purpose. A migration you can only run once is a migration you cannot fix.

What it does

The parts that matter

Merged, not deleted
Duplicates are combined and the original rows kept, so a wrong merge is reversible
Reported first
Counts for imported, merged and needs-review, reviewed before anything is committed
Needs-review queue
Ambiguous cases go to a person instead of being guessed at by a rule
History preserved
Past jobs, invoices and notes stay attached to the right customer
Repeatable
The import can be run again from scratch, so a fix means re-running rather than hand-editing
Rollback
Available until you sign it off
Before and after

What comes out of an old system, and what it is for.

A data import screen with messy incoming records on the left, field mapping in the middle, and cleaned matched records with counts on the right
Example: The import: what came in, what matched, what a person still has to look at.
An operations dashboard with revenue, quotes sent, open jobs and overdue invoices above two simple charts
Example: Why it is worth the care. Reporting only works when the data underneath it is clean.
When I would tell you not to

If the old system still does the job and the only complaint is that it looks dated, moving the data is an expensive way to buy a new interface.

Migrate when the old system is being switched off, when it cannot talk to anything else, or when it is stopping you doing something you need to do. Not because it is ugly.

Start here

Is your data the thing keeping you on old software?

Tell me what system it is in and roughly how many records. I will tell you in writing whether the move is straightforward, awkward or genuinely risky — free.