Nirmitee.io
Delivery journal · The context behind the implementationDiscuss a similar challenge ↗

EHR Data Migration Case Study: Sizing a Legacy EMR Move to a FHIR Platform

September 25, 2026
9 min read
Written by
Jitendra Choudhary
Jitendra Choudhary

CTO & Co-Founder

CTO & Co-Founder at Nirmitee.io. Architects healthcare integrations across FHIR, SMART on FHIR, ABDM and NHCX, writing from production experience taking hospital software from sandbox to go-live.


At a glance

  • Client: a North American home health care provider with its own digital platform team.
  • Situation: moving its clinical programs from an older EMR onto a new FHIR-based clinical platform.
  • What Nirmitee did: established the size and shape of the historical data with the provider before any pipeline was built, wrote the migration code, prototyped reporting across the new FHIR store and existing databases, and added two standard mental health questionnaires to the FHIR server.
  • Key finding: the legacy data was much smaller than first assumed, stable in structure and closed to new changes, so a single scripted run replaced a planned pipeline.
  • Stack: a FHIR server that stores its resources in PostgreSQL, Python, PostgreSQL foreign data wrappers and materialized views, Apache Superset.
  • Period: June to September 2025, one Nirmitee engineer.
  • Status at end of engagement: migration code submitted for review; reporting demonstrated as a proposal. This case study does not cover the final migration run or production use.

Summary

This EHR data migration case study shows what happens when a provider measures its legacy data before paying for a migration architecture. A North American home health care provider was replacing an older EMR with a new FHIR-based clinical platform. At the outset the working assumption was roughly 1 TB of historical records, a figure that points towards staged loads, checkpoints, change capture and a pipeline that someone has to operate.

A Nirmitee engineer took the historical data and reporting questions as owned tasks. One working session with the provider replaced the assumption with facts: the data set was much smaller than assumed, the old schema was static, and nothing new would be written to the old database. The design changed accordingly, from a pipeline to one scripted run with a per-record error policy that the provider agreed in advance. The heavy pipeline was never built, because the data did not need it.

The same engagement produced a reporting proof of concept that queries the new FHIR store and existing databases in place, without a separate warehouse, and added the GAD-7 and PHQ-9 questionnaires to the FHIR server. Nirmitee delivers this kind of work through its healthcare data migration services.

The business challenge

Replacing an EMR raises two questions that the new system does not answer by itself. What happens to the years of records held in the old system? And how will managers report across the new FHIR store and the operational databases that sit beside it?

At the start of the engagement the task list from the kickoff calls assigned both questions to the Nirmitee engineer. For historical data, the question was whether to import old records into the new platform's own tables or hold them in a separate archive store. For reporting, the question was how to combine FHIR data with separate databases: an ETL pipeline, database replication, or something lighter.

Neither question could be answered well without knowing the size and shape of the data, and at that point nobody had measured it. The 1 TB figure was a hypothesis, not a count.

What was at stake

A migration designed on an assumed size tends to go wrong in one of two directions, and both are expensive.

  • Paying for infrastructure the data never needed. A 1 TB design brings batching, checkpoints, change capture, reconciliation services and the operations to run them. If the real data set is small and closed, that spend buys nothing.
  • Delay while the design is reworked. When the first real extract contradicts the assumption, the architecture is revised in the middle of delivery, and the cut-over date moves.
  • Clinical and compliance risk. Records that fail silently, or stop a run halfway, leave gaps in the history clinicians rely on. Someone has to own every record that does not load.
  • Reporting left behind. Managers still need reports on day one. A warehouse project started alongside a migration doubles the risk and often delays both.

Nirmitee's guide to legacy EHR data migration sizing sets out the measurements to take and the decisions that follow. This page shows those decisions being made on one real migration.

The approach

The work followed four principles.

  • Measure before designing. Size, schema stability, ongoing writes and mapping ownership were settled with the provider before the migration design was fixed.
  • Choose the lightest design the facts allow. Pipeline infrastructure is justified by volume, change or drift. Where none of the three is present, a scripted load is simpler to test, easier to review and cheaper to run.
  • Agree failure handling with the business. What happens to a record that fails is a decision about who fixes bad data, so it was agreed with the provider rather than left to the code.
  • Report where the data already lives. Before proposing a warehouse, the engineer tested whether reporting could run in place on the databases that already existed.

This is the sizing-first discipline Nirmitee brings to healthcare data engineering work: the architecture follows the measured data, not the other way round.

What was delivered

  • A measured basis for the migration design. A working session in July 2025, where the engineer demonstrated the proposed approach, established the four facts that set the design: a much smaller data set than assumed, a static schema, no ongoing changes in the source database, and a clear owner for the field mapping. Proof: the engineer's session notes from July 2025.
  • An agreed migration design. A one-time scripted load, a field mapping supplied by the provider's team or built by the engineer, and per-record failure isolation with a follow-up list for the provider's team. The provider preferred to migrate in one run.
  • Migration code. A pull request for the historical data migration, opened in August 2025 and taken into code review.
  • A reporting proof of concept. PostgreSQL foreign data wrappers, materialized views and Apache Superset, built in July 2025 and demonstrated as a proposal.
  • Standard questionnaires on the FHIR server. A pull request adding the GAD-7 anxiety and PHQ-9 depression instruments, with a combined demonstration of the historical data and FHIR server work scheduled with the provider for late August 2025. The questionnaire work continued into September.

Every item above was submitted, reviewed or demonstrated. The records available end before the final migration run, so this page makes no claim about it.

How it works

Historical migration

The migration reads the legacy extract, applies the field mapping, and writes the records to the new FHIR-based platform in a single run written in Python. The error policy was agreed up front: if one record fails, only that record fails, and it is set aside in a follow-up list for the provider's team to correct. One bad record never stops the run, and no record is skipped without trace.

The design leaves out what the facts made unnecessary. A static schema meant no handling for column drift. A closed source database meant no change capture and no catch-up load. A small data set meant no staged batches, checkpoints or pipeline to operate. Nirmitee's article on change data capture for EHR data covers the case where the old system stays live during a parallel run and that answer changes.

Sizing questionWhat the provider confirmedEffect on the design
How big is the data?Much smaller than the 1 TB first assumedOne run instead of staged batch loads with checkpoints
Does the schema still change?No, the legacy schema is staticOne mapping, no drift detection
Is anyone still writing to it?No ongoing changes in the source databaseNo change data capture, no catch-up load
Who owns the field mapping?The provider's team, or the engineer if neededMapping ownership settled before coding
What happens to a failed record?Fail that record only and list it for follow-upPer-record isolation; the run always completes

Reporting in place

The reporting proof of concept avoids a separate warehouse. PostgreSQL foreign data wrappers let one reporting database query the FHIR server's PostgreSQL database and other operational databases where they are. Materialized views flatten nested FHIR resources into report-shaped tables that refresh on a schedule. Apache Superset reads from those views for dashboards. Nirmitee's article on clinical dashboards on FHIR with Superset shows the approach in more detail.

Assessments as FHIR data

The PHQ-9 and GAD-7 instruments were added to the FHIR server as Questionnaire resources, so completed assessments are stored as QuestionnaireResponse resources and can be queried by the same reporting layer as the rest of the record. Nirmitee's FHIR integration services cover this kind of FHIR server work.

Lessons that transfer to your project

1. Measure the data before you choose the architecture

A 1 TB assumption leads to batching, checkpoints, change capture and a pipeline to operate. One working session showed a static, closed and much smaller data set, and the right tool became a script that runs once. Ask four questions before designing: how big is it, does the schema change, is anyone still writing to it, and who owns the mapping.

2. Agree the error policy with the business before the first run

"Fail the record, not the run, and list it for the clinical data team" is a decision about who fixes bad data, not a coding detail. Agreeing it early meant the script's behaviour on a malformed record was already accepted by the people who would have to correct it.

3. A closed legacy system does not need change capture

If the old EMR receives no new data after cut-over, there is no delta to chase. Confirm this in writing, because it removes a whole class of reconciliation work. If the old system stays live during a parallel run, the answer changes and the design must change with it.

4. Report where the data already lives before building a warehouse

Foreign data wrappers and materialized views can give a small or medium provider useful reports across a FHIR store and its operational databases without a warehouse project. The limits are real: queries run against production databases, so heavy views need off-peak refreshes or a read replica. A data pipeline or warehouse becomes worth its cost when volumes, sources or refresh demands outgrow that.

5. Treat a migration as a product decision, not only a technical task

The most valuable output of this engagement was not code. It was a design that matched the data, settled about a month into the engagement, before money went into infrastructure. That decision belongs with the provider's leadership, and it depends on facts only the provider holds.

Where this applies

These are applications of the pattern, not work delivered for this client.

  • Home health and hospice agencies changing EMR vendors, where historical visit and assessment records must stay available after cut-over.
  • Post-acute and behavioral health providers moving to a FHIR-based platform who need reporting across the new FHIR store and existing billing or scheduling databases.
  • Digital health companies onboarding a provider's legacy data into their own FHIR platform during implementation, often alongside EHR and EMR integration work.
  • Any EHR switchover where the size, stability and ownership of the legacy data have not yet been measured. Nirmitee's article on EHR migration and FHIR data portability covers vendor exit and export options.

Where data has to keep moving between live systems rather than once, see the FHIR data synchronization case study for a European regional cancer care network, and Nirmitee's wider healthcare interoperability services.

For your engineering team

  • Migration: one-time Python load from a legacy extract to a FHIR-based platform; static source schema; no change data capture; per-record failure isolation with a follow-up list.
  • Mapping: field mapping from the old EMR to the new model, owned by the provider's team or built by the engineer; settled before coding.
  • Reporting: PostgreSQL postgres_fdw foreign tables over the FHIR server database and operational databases; materialized views for flattened FHIR data; Apache Superset dashboards; refresh off-peak or from a read replica.
  • Standards: HL7 FHIR R4 Questionnaire and QuestionnaireResponse for the GAD-7 and PHQ-9 instruments.
  • Process: code through pull request review; approach demonstrated to the provider before implementation.

Working with Nirmitee

Nirmitee is ISO 27001:2022 certified, is HIPAA-enabled and signs BAAs. It can join a migration at the sizing stage, before the design is fixed, and through its healthcare product engineering practice it can work inside a provider's or platform's own team and review process.

A first conversation is a sizing call. Bring what is known about the legacy system: its approximate size, whether it will stay live after cut-over, whether its schema still changes, and who will own the field mapping. Nirmitee will set out which migration design those answers support, and what can be left out. Book a sizing call.

Share this case study

Related Case Studies