Diffing two versions of a FHIR resource sounds like a solved problem. Then you run a generic JSON diff over an Observation from two servers and the output is a wall of meta.lastUpdated, id, and reference-URL changes that swamp the two fields that actually differ clinically. The trick is not writing a smarter diff — it is knowing what to ignore. The site's Resource-vs-resource comparator applies a curated ignore list by default. For the wider setting, FHIR resources for behavioral health teams has more.
What Makes FHIR Diffing Special
- Every resource carries
metathat changes on every version bump - References use ids that differ per server
- Extensions live alongside base fields and add clutter
- Bundles contain resources whose entry order may not be semantic
- Some fields are auto-computed and change without meaningful intent
A generic JSON diff surfaces all of these. A FHIR-aware diff filters most of them out.
The Default Ignore List
For most workflows:
meta.versionIdmeta.lastUpdatedmeta.sourceResource.id(when comparing conceptually the same resource across servers)text.div(narrative — usually derived)
That is five fields. Ignoring them clears most of the noise on the resources most people diff most often.
For the specific safety around meta.lastUpdated, ignoring meta.lastUpdated without ignoring the wrong things is the entry.
The Reference Case
Reference URLs contain resource ids. Two versions of the same Observation from two servers reference "the same" Patient with different ids. A diff that flags every reference change floods the output.
Options:
- Ignore
Reference.referencevalues entirely (loses too much) - Diff by
Reference.identifierwhen present (better) - Rewrite ids to a canonical form using a mapping (best but requires setup)
The comparator supports the middle option out of the box. For teams with an id-mapping table, the last option is worth the setup.
The Extension Noise
Two resources may carry different extensions without carrying different clinical meaning. Two US Core Patients from two systems may have race extensions in different order — same content, different representation.
The right diff normalizes extension arrays by URL before comparing. That is one of the differences between a generic JSON diff and a FHIR-aware one.
For the structural vs semantic split, structural diff vs semantic diff for FHIR JSON is the entry.
The Bundle Alignment Case
Diffing two Bundles that contain the same resources in different order produces noise. Every entry looks changed because indexes shifted.
The pre-diff step for Bundles: align entries by fullUrl or by Resource.id. Then diff the aligned pairs. For the mechanic, diffing Bundles: aligning entries before comparing is the entry.
The Path Format Choice
Diff output uses paths to describe changes. Common formats:
- JSON Pointer —
/name/0/family - JSONPath —
$.name[0].family - FHIRPath —
Patient.name[0].family
FHIRPath is the most readable for humans familiar with FHIR. The comparator emits FHIRPath by default and JSON Pointer on request.
Machine-Readable Output
The diff should be machine-readable so downstream tools (PATCH generation, alerting rules) can consume it. JSON Patch (RFC 6902) is the standard. For the mechanic, PATCH generation from a JSON diff is the entry.
The Base Rule
The right FHIR diff is not the most detailed one. It is the one that shows the intentional changes and hides the incidental ones.
Getting the ignore list right is a five-minute setup that saves fifty hours of reviewing noise.
The Short Version
Ignore meta by default. Handle references by identifier when possible. Normalize extensions by URL. Align Bundle entries before diffing. Emit FHIRPath for humans, JSON Patch for machines. The comparator does most of this out of the box.

Sources
- HL7 canonical R4 Resource base spec covering meta and id - HL7 canonical R4 Resource base spec covering meta and id
