A migration does not have to replace the database and application at the same time. In many real systems, the database is shared by reports, support tools, old integrations, and years of operational knowledge.
The database is an external contract
Table names, nullable columns, identity records, audit fields, and reference data may look like implementation details. Once other systems depend on them, they become contracts.
That does not mean every old decision is permanent. It means the migration should identify the dependencies before changing them.
Separate the contexts
The modern application makes its database responsibilities explicit. The main application context is separate from logging and monitoring contexts. This separation clarifies ownership and prevents operational logging from becoming indistinguishable from business data.
It also provides a useful failure boundary. A logging problem should be diagnosable without pretending that the clinical or business database has the same failure.
Resolve configuration before registering EF Core
The application resolves database settings before registering its contexts. It can select SQLite for local development and SQL Server for deployed environments, while exposing the resolved provider and target through startup diagnostics.
That decision keeps provider selection in configuration rather than scattering connection-string checks throughout controllers.
Compatibility before cleanup
During migration, compatibility code is often the safest temporary tool. A normalisation repair, a legacy column mapping, or a defensive null check can allow the new application to operate while the underlying data is cleaned up through a controlled process.
The important distinction is between a documented compatibility measure and an accidental permanent workaround. Every compatibility decision should record:
- which old contract it protects;
- what failure it prevents;
- how it is tested;
- whether it has a retirement condition.
Migrations are not magic
Entity Framework can describe a model and generate migrations, but it cannot infer every operational rule from a legacy database. Schema comparison, backups, seed data, permissions, and production rollout still need human decisions.
The safest migration work makes the application model, live schema, and deployment procedure visible to one another.
The lesson
Modernise the application boundary first when the database is valuable and widely depended on. Make data access explicit, isolate responsibilities, and retire compatibility deliberately. A staged database strategy is often less dramatic than a replacement, but much more survivable.