Uuid Toolkit

Keeping UUIDs Stable Across Environments

A UUID is stable by construction — the id itself never changes. What is not stable, and what teams get wrong, is the mapping from a resource in one environment to the same resource in another. Dev, staging, and production each have their own copies; getting them to agree on what "the same Patient" is takes discipline. The site's fullUrl UUID spinner generates the ids; environment-stability is an operational pattern on top. For the wider FHIR framing, more FHIR buyer guides has more.

Why Stability Matters

  • Automated tests need known UUIDs to assert against
  • Data migrations need consistent ids to prevent duplicates
  • Support engineers need to refer to the same Patient across environments
  • CI/CD pipelines need to deploy predictable fixture data

Without stability, every environment becomes its own island and cross-environment work becomes guessing.

Two Real Strategies

  • Deterministic UUIDs — derive the UUID from a stable business identifier
  • Fixture files — reserve a set of well-known UUIDs for cross-environment use

Both work. They serve different scopes.

Deterministic UUIDs (v5)

UUID v5 is generated from a namespace UUID + a name (string). The same namespace + name always produces the same UUID. It is one-way — you cannot reverse the UUID to get the input — but it is deterministic.

Use case: mapping an external system's MRN to a stable FHIR resource id. v5("mrn-namespace-uuid", "MRN-12345") produces the same UUID everywhere.

Cost: the input has to be stable. If the MRN changes, so does the UUID.

Fixture Files

For test data, reserve a set of well-known UUIDs and check them into version control. Every environment loads the same fixture data with the same ids.

Fixture files work for reference data (test patients, test practitioners) but not for real business data.

For the collision question, collision-free UUIDs in a multi-tenant FHIR deployment covers the isolation pattern.

What Not To Do

  • Do not regenerate UUIDs when copying data between environments — the mapping is lost
  • Do not use different UUID versions in different environments — that produces cross-environment inconsistency
  • Do not include environment name in the UUID — that leaks and does not survive migration
  • Do not use timestamps as inputs to v5 — timestamps drift; the UUID does not

The Identifier Fallback

If deterministic UUIDs are not viable, Resource.identifier[] is the safe fallback. Every resource carries its business identifier, and cross-environment lookups can go through the identifier system. The Resource.id differs per environment; the identifier[].value does not.

For the distinction, [the difference between resource id and identifier[]](/uuid-toolkit/resource-id-vs-identifier-array/) is the entry.

Snapshot Migration

Copying a snapshot from staging to production preserves UUIDs. That works when the snapshot is authoritative. It does not work when both environments have their own writes — merging conflicting UUIDs is a manual task.

For the migration pattern, UUID hygiene for teams migrating from sequential IDs is the entry.

Version Control Discipline

Fixture UUIDs live in version control. Every commit that changes them should be reviewed. Every environment reads the same file.

That single discipline — treat fixture UUIDs like code — prevents most of the cross-environment drift teams experience.

The Short Version

Deterministic v5 for business-identifier-based mapping. Fixture files for test data. Never regenerate on migration. Fall back to identifier[] when UUIDs cannot be made deterministic. Treat fixture UUIDs like code.

Halftone-comic diagram of stable UUID strategies — v5 deterministic derivation from a namespace + business key, plus fixture files for test data — with cross-environment mapping annotated, with dot pattern shading and hot-pink accents on cream paper

Sources