Write the tests before you touch the code
Characterization tests aren't unit tests you forgot to write. They're a behavioral snapshot of the legacy app, and they're the only honest definition of “nothing changed.”
Every migration here runs the same six steps in the same order, and the third one — Characterize — is the one people are tempted to skip. Don't. It's the step that turns “trust me” into “here's the suite.”
A characterization test doesn't ask whether the code is correct. It asks whether the new code does exactly what the old code did — bugs included.
Why bugs included
Because behaviour nobody remembers building is still behaviour something depends on. The goal of a migration is not to improve the app; it's to move the platform and change only what the platform forces. If a quirk shipped on net48, it ships on net10.0, and the test proves it. Fixing it is a different, later, opt-in conversation.
What it looks like in practice
Pin the inputs, capture the outputs, lock them in. Then migrate, and run the suite against the new code. Acceptance is that result — green — not a feeling, not a demo, not the model's confidence.
That's the safety net the front page keeps pointing at. It isn't a slogan; it's a file in your repository you can run yourself.
Wondering what your own migration would read like?
Twenty minutes, no pitch. I’ll tell you whether your app is a fit and roughly what the path looks like.