FHIR validation is often talked about as one activity, but in practice it is two very different jobs stapled together. Structural validation asks whether your resource is legal FHIR. Terminology validation asks whether the codes inside it actually mean anything. Confusing the two is the source of a lot of wasted debugging.
The short version is that structural comes first, because a resource that fails structurally is not worth spending terminology cycles on. For the wider view of why validation matters at all, what really breaks when you skip Bundle validation sets the stage, and the healthcare interoperability hub is where the rest of the validation series lives. This piece stays on the choice between the two lanes.
What Structural Validation Actually Does
Structural validation is a shape check. It reads your resource and asks whether every element exists in the base spec, whether cardinality is respected, whether types match, and whether required elements are present. It looks for resourceType, checks that Observation.status is one of the allowed strings, verifies that Patient.name[0].family is a string and not a list.
It does not care whether the LOINC code inside Observation.code is actually a valid LOINC. It just checks that the Coding shape is present, with system, code, and optionally display. Structural validation is fast, deterministic, and can run offline. A pass through the site's Bundle validator is doing structural work in milliseconds.
What Terminology Validation Adds
Terminology validation is where a resource meets the wider clinical world. The validator takes each Coding in the resource and asks a terminology server whether that code is real, active, and bound to the value set the profile expects. Observation.code bound to LOINC? The validator looks up the code in LOINC and confirms it. Condition.code bound to SNOMED CT? Same conversation, different code system.
Terminology work is slow. It talks to an external server, it depends on version metadata, and it can return partial answers when a value set is very large. It also has a different failure mode: the code exists, but is deprecated, or the code exists but is not in the specific value set the profile requires.

Why Structural Runs First
Sequencing matters. If a resource has a broken shape, terminology validation cannot even locate the Coding to check. Running terminology first would waste API calls on resources you already know are invalid.
Structural also has a much higher signal-to-noise ratio for early debugging. A structural error means "you have a bug in your generator". A terminology error often means "the world moved and your value-set binding needs an update". The first is your problem to fix immediately; the second is a slower conversation about profile versions.
When You Actually Need Both
Not every use case needs terminology validation. A pure passthrough that reads FHIR from one system and writes it to another may only need structural to make sure the shape survives. A decision-support engine that keys off specific codes absolutely needs terminology, because a deprecated code silently breaks rules.
The right calibration is per pipeline stage. Ingest checks are structural. Storage may run both. Egress to a certified partner usually enforces the profile's terminology bindings too. Related to this: the structural mistakes we see in real-world Bundles shows what falls out of the first lane, and the batch vs transaction question covers how these lanes behave when you package resources together.
The One-Line Rule
If it fails structurally, stop. If it passes structurally, then decide whether terminology matters for this stage. That order is how you spend validator time in the ratio the errors actually deserve.
Sources
- HL7 FHIR Validation Spec (Structural and Terminology) - HL7 FHIR core specification, Validation section covering both structural checks and coding/binding validation layers