← Writing · Essay · 6 min · 30 April 2026
A migration is not done if the next developer can undo it.
Most freelance cloud migrations are data movement. Real migration is handover. Three rules from a 9,778-row Supabase migration that the client owns end to end.
I finished a three-week cloud migration in April 2026. Moved a personality assessment SaaS off Lovable Cloud onto a clean Supabase + Cloudflare stack. Seventy-two live users with their bcrypt password hashes intact, no forced resets. 9,778 rows across 27 tables. Zero data loss.
That is not the part of the project I am proudest of.
The part I am proudest of: the CI/CD pipeline now rejects any deploy that still points at Lovable. The 858-line playbook documents every account ownership transfer, every secret, every dependency. If I never speak to this client again, his next developer can still operate the system. He owns every production account. Not me.
Most of what calls itself “cloud migration” on freelance platforms is data movement. SQL exports, SQL imports, change some URLs, ship.
Real migration is handover.
The temptation
Once you have the data moved, you can stop. You sent the CSV. You restored the schema. The site loads. The client clicks around, says it works, pays the invoice.
A week later: the client cannot figure out which secret in which Cloudflare dashboard rotates the Stripe webhook. A month later: someone helpfully redeploys the original repo and the site silently starts hitting Lovable’s database again, because the env var was not changed and nobody noticed. A quarter later: the next developer asks “where does the email send from” and the answer is “Resend, I think, but I would have to dig.” That is the migration not actually being done.
Rule one: own every account at the end, then transfer
During the migration, I created Supabase, Cloudflare, Stripe, Resend, Turnstile under my own identity. That is faster than trying to do it through the client’s account in real time. At the end, ownership transferred to the client. Documented. Two-factor on his phone. My access revoked or visible. The client is the root.
This sounds like a small thing. It is the difference between “Mohammad knows how this works” and “the client knows how this works.” Only the second is a finished migration.
Rule two: write the playbook
Not as documentation. As a runbook the next developer can execute.
Where does the data live. What rotates what secret. What does each edge function do. Where does the email send from. What is the exact command to deploy. The MeetYourMind playbook is 858 lines. Most of those lines are not interesting. That is the point.
If the playbook is interesting to read, it is wrong. It should be exhaustive, boring, and copy-pastable. A good playbook reads like an instruction manual, not an essay.
Rule three: gate the regression
Add a CI guard-rail that refuses to ship if the most likely failure mode happens.
For MeetYourMind, the most likely failure was someone redeploying the old Lovable-pointing code. The pipeline now checks the build output for Lovable URLs. Any match fails the build. The next developer cannot accidentally undo the migration.
This rule exists because of an honest observation: the next developer is going to be tired, on a deadline, and may not have read the playbook all the way through. The migration has to survive that.
The harder part
The hardest decisions during the migration were not technical. The CSV export from Lovable used semicolon delimiters and JSON arrays where Postgres arrays were expected. That was Python and an hour. Two Supabase generated-column bugs that broke import. That was a stage-then-insert pattern and eight iterations to land. The auth migration with bcrypt hashes intact. That was reading the GoTrue source and one careful migration script.
All hard, all bounded. None as hard as deciding “this migration is not done” when the client is already saying it looks great.
A test
Here is the test I use now. If the client emails me in six months asking “where does X live,” and I can answer “page seventy-two of the playbook” without opening anything, the migration was done.
If my answer starts with “let me check my notes,” the migration was not done. It was data movement.
This is true even when the technical work was excellent. Maybe especially then.
Filed under Migrations · Supabase · Handover
Migration stuck or risky?
If your stack is locked into a managed wrapper and you want clean ownership of Supabase, Stripe, Cloudflare, and your data, this is the shape of work I do.