A dull checklist beats a brave team every single time.
The part that surprised us
Migrations fail in the details: serialized data, hard-coded URLs and that one plugin nobody remembers installing. We make an inventory before we move anything, then we move it twice, once for practice. The rehearsal always finds something. Finding it on a Wednesday at a desk is much better than finding it on launch night with the client watching.
Good defaults are a kindness to the next person. If a feature only works after someone reads a long page of setup notes, then for most sites it does not work at all. We would rather ship something modest that behaves well on a fresh install than something impressive that needs a careful afternoon before it shows anything.
If you cannot explain why it is fast, it is only fast today.
Priya Raman
What we changed
The numbers moved less than we hoped, and that is worth saying plainly. Posts like this tend to end with a dramatic chart, but real work mostly produces a steady, unglamorous improvement. The page got a little faster, the logs got a lot quieter and the support inbox stopped filling up on Monday mornings. We will take that.
Logs are the cheapest witness you have. Before anyone proposes a theory, we turn on debug logging on a copy of the site, repeat the problem once and read what it left behind. Half the time the answer is sitting there in plain words, and the other half we at least know which theories to stop discussing.
How we tested it
Permissions tend to drift. A role that made sense at launch picks up one extra capability each year until nobody can say who is allowed to do what. We list the roles, list the people and compare them with what the job actually needs. It is a dull hour, and it has found more real problems than most scanners.
Every feature has a running cost, even when nobody touches it. It needs testing on each upgrade, explaining to each new editor and checking in each review. When a client asks for something small, we ask who will look after it in two years. Sometimes the honest answer is that it should stay a good idea.
What we would do differently
Clients rarely remember the clever parts. They remember whether the site stayed up, whether the invoice matched the estimate and whether someone answered when it broke. We try to write code with that in mind. A plain solution that a tired person can repair at the end of a long day beats an elegant one.
Writing it down was the last step and possibly the most useful one. A short note about what we tried, what failed and why we stopped has saved us more time than any tool. When this comes up again, and it will, someone will search the repository, find these paragraphs and skip the whole expensive afternoon.
The boring fix
We started by reading the code rather than the documentation, because the code is what runs. Most of the surprises in a WordPress project live in the gap between what a function promises and what the theme actually asks of it. An hour with the templates open told us more than a week of status meetings ever had, and it gave us a list of real questions to bring to the client.
Priya Raman Nov 06, 2026
I disagree slightly on the rollback plan, but the rest matches what we do.
Omar Haddad Nov 06, 2026
Thanks for showing the failed attempts as well. That is the useful part.
Aisha Khan Nov 06, 2026
Could you share the checklist as a plain text file? I would like to adapt it.
Ravi Menon Nov 06, 2026
We do something similar with WP-CLI in our deploy step and it has been solid.
Omar Haddad Nov 06, 2026
The point about small diffs is the one I keep having to repeat at work.
Priya Raman Nov 06, 2026
Sending this to a colleague who is about to start a migration next week.
Ravi Menon Nov 06, 2026
Nice. One small correction: that hook fires earlier than the post suggests.
Grace Tanaka Nov 06, 2026
This saved me an afternoon. The bit about testing the cold path first is going on the team wiki.