A Successful Migration Can Still Be Wrong
Moving the report is not the same as preserving its behavior.
Behind the Dash
- •Several controls no longer affect what they are supposed to affect. Nothing is visibly broken until someone relies on the report.
Front of the Dash
- Report
- Opens
- Charts
- Render
- Numbers
- Look familiar
- Migration
- Marked complete
The request sounds straightforward.
"Move these reports to the new environment."
There is usually a checklist. The report exists. The data source resolves. The visuals render. The important calculations still return values. Maybe someone compares a few totals between the old version and the new one.
Everything looks good, so the migration gets marked complete.
That is where I think a lot of migration plans stop too early.
A report is not a bag of objects. It is a network of relationships.
A filter targets something. A control changes something. A calculation depends on a particular grain. A permission rule determines which rows exist before the user ever sees them. A scheduled process assumes another process finished first.
You can successfully migrate every individual component and still break the product.
That is the uncomfortable part.
The report opens.
Nothing throws an error.
The chart still has bars.
The dropdown still drops down.
It just does not affect the thing everyone assumes it affects.
Those failures are more dangerous than obvious failures because obvious failures stop people.
A blank dashboard gets reported.
An error message gets investigated.
A report that quietly ignores one of its controls can be used for weeks.
The clean surface becomes part of the problem. If everything looks normal, users reasonably assume everything is normal.
This is especially easy to miss during migrations because teams tend to validate artifacts instead of behavior.
Did the report move?
Yes.
Did the model build?
Yes.
Did the scheduled job run?
Yes.
Did the dashboard load?
Yes.
Those are useful checks, but they prove that the pieces exist. They do not prove that the relationships between the pieces survived.
The better migration question is not, "Did we move everything?"
It is, "What behavior did the old system guarantee, and have we proved the new system still guarantees it?"
That changes the test plan.
Now you have to exercise the controls instead of merely confirming they exist.
Change a filter and verify the expected population changes.
Test permissions with users who should see different slices of the data.
Check boundary dates, empty states, unusual configurations, and combinations of options that nobody uses in the demo.
Verify that a report is not merely returning the right total, but returning it for the right reason.
This matters because migrations have a nasty habit of exposing assumptions that were never documented.
Maybe the old platform handled a relationship implicitly. Maybe somebody configured something manually years ago. Maybe a default changed. Maybe the new system is technically behaving exactly as designed, but that design is different from the behavior users depended on.
Calling those problems migration bugs misses the larger lesson.
They are undocumented contracts.
And migrations are where undocumented contracts go to die.
This is also why rebuilding everything during a migration can be so tempting and so dangerous. A clean new architecture feels like an opportunity to remove years of accumulated mess.
Sometimes it is.
But some of that mess represents actual business behavior. If nobody distinguishes accidental complexity from necessary behavior before rebuilding, the new system can become beautifully consistent and operationally wrong.
The answer is not to preserve every historical quirk forever.
It is to know which behavior you are intentionally changing.
A good migration should be able to say three things clearly: what stayed the same, what changed on purpose, and how we proved the difference.
Without that, "migration complete" mostly means the new system stopped producing obvious errors.
That is a very low bar for something people may use to make real decisions.
Before signing off on the next migration, do not ask whether every report made it across.
Ask whether every important relationship did.
Related essays
The Dashboard Is Not Late. The Business Is Still Processing.
When two reports disagree, the first instinct is usually to blame refresh timing. Sometimes that is correct, but "refresh timing" is often shorthand for a chain of independent processes that finish at different times. Until teams model that chain, telling users when the data is ready is mostly guesswork.
Fresh Data Is Not the Same as Current Data
Teams often define data freshness by how frequently a pipeline runs. Operational users care about something else. They care whether the number on the screen reflects the business event they are trying to understand. Those are not always the same thing.
Not Every Report Is Analytics
A list of reports can look like a list of analytical products. It often is not. Some exist to help a person understand the business. Others quietly feed downstream processes, satisfy external requirements, or keep operations moving. Treating them all as dashboards creates expensive mistakes.