# Migrations and compatibility The API applies EF Core migrations on startup and performs guarded legacy-schema reconciliation for SQLite and MariaDB/MySQL compatibility. Fresh and repeated disposable SQLite migration chains are the normal local verification; generated provider SQL is not proof of a live production migration. The `Job`/`JobApplication` opportunity cutover is an expand/verify/contract change. Keep dual writes and compatibility reads until a production report and restored-backup rehearsal pass, then observe one release before removing legacy columns in a later migration. Never edit the history of an applied migration. ### Production cutover validation After the additive backfill, run these read-only MariaDB/MySQL checks against production and a restored backup. Every result must be `0`; do not turn them into an update script. ```sql SELECT COUNT(*) AS missing_job FROM JobApplications WHERE JobId IS NULL; SELECT COUNT(*) AS missing_target FROM JobApplications a LEFT JOIN Jobs j ON j.Id = a.JobId WHERE a.JobId IS NOT NULL AND j.Id IS NULL; SELECT COUNT(*) AS owner_mismatch FROM JobApplications a JOIN Jobs j ON j.Id = a.JobId WHERE NOT (a.OwnerUserId <=> j.OwnerUserId); SELECT COUNT(*) AS field_mismatch FROM JobApplications a JOIN Jobs j ON j.Id = a.JobId WHERE NOT (a.CompanyId <=> j.CompanyId) OR NOT (a.JobTitle <=> j.JobTitle) OR NOT (a.Location <=> j.Location) OR NOT (a.JobUrl <=> j.JobUrl) OR NOT (a.Description <=> j.Description) OR NOT (a.Salary <=> j.Salary); ``` Observe one release after the checks pass, then change reads, re-run the checks on a fresh restore, and only then make `JobId` required and remove duplicated opportunity columns in a later migration.