The migration was meant to be mechanical. Python 2 to 3, a codebase someone else wrote years earlier, a test suite that passed.
It passed because it tested what the original authors knew they had built. About what the system had quietly learned to do in production over the years it ran, it said nothing.
The parts that hurt were not syntax. They were behaviours that had not been written down: a string that was sometimes bytes depending on which path produced it, ordering that downstream code depended on without declaring it, an edge case someone patched once and left undocumented because that week the patch was clear to them.
The diff does not show any of that. Production shows all of it.
The cost was time spent in the wrong place. I had scoped the work as a language migration, then found myself doing archaeology: reading logs, tracing live calls, asking people what the system was supposed to do and watching them disagree with each other.
So now, before I agree to any rewrite, I ask what the system does that nobody can state out loud. Not the documented behaviour. The behaviour that exists because of a production incident three years ago, or a client who needed one specific thing, or a bug that became load-bearing once something downstream started depending on it.
If the answer is "we're not sure", the first phase is characterisation rather than a rewrite: write tests against the current system until the tests describe reality instead of intent. Slow and unglamorous, and probably the only thing that keeps the rewrite scoped instead of open-ended.
The other questions I ask are about blast radius, about who owns the fallback when it goes wrong, about whether the rewrite solves a problem the business feels or one only the engineers feel, and about what happens if it stops halfway, because it often does.
I wrote more about how I approach inherited systems over on the blog, if that's useful: https://polycratia.com/c/766896f9
If you have done one of these, tell me what your old system turned out to be doing that the team could not explain.