Correctness is behavioral
During a migration, a new report may look polished and use the expected columns while producing materially different totals. Small implementation differences in joins, null handling, rounding, date boundaries, grouping, or sorting can change decisions downstream.
Verification therefore needs a definition of correct behavior grounded in representative inputs and outputs—not simply the absence of runtime errors.
- Values and calculated totals
- Labels, grouping, and ordering
- Filters and permission rules
- Exported file structure and downstream use
Historical output is useful evidence—with limits
Historical reports can reveal real-world combinations that a synthetic test set misses. On a recent modernization project, more than 1,900 historical report cases were used to verify the modernized implementation.
History is not automatically authoritative. Source records may be incomplete or inconsistent, and some old output may encode a known defect. Those cases should be isolated and explained rather than forced into a misleading pass result.
Investigate discrepancies, do not average them away
A useful comparison process makes each material difference traceable. Some discrepancies identify defects in the new implementation. Others reveal undocumented legacy behavior or unreliable source data.
The result should state what matched, what changed intentionally, what could not be verified, and why the available evidence supports acceptance.
- Automated data and cell-level comparison
- Representative edge-case tests
- Reviewable discrepancy records
- Explicit known exceptions
Make verification part of the architecture
Parity should not be a final-week testing exercise. Isolating calculations, queries, and report-generation logic makes them easier to test throughout implementation.
A migration becomes safer when stakeholders can see a growing body of reproducible evidence instead of waiting for a final visual review.