Uuid Toolkit

UUID Hygiene for Teams Migrating From Sequential IDs

Migrating a FHIR deployment from sequential integer ids to UUIDs is a two-quarter project done well and a two-year cleanup done poorly. Every existing reference has to be rewritten, every downstream cache invalidated, every audit log mapped between the old and new ids. The site's fullUrl UUID spinner generates the new ids; the migration around them is where the discipline matters. For the wider FHIR framing, side-by-side FHIR product breakdowns has more.

Build The Id Map First

  • Every existing resource gets a UUID
  • The old sequential id and the new UUID live together in a mapping table
  • The mapping is authoritative for the migration duration
  • References in Bundles get rewritten via the mapping

Do not delete the mapping table after migration. It is your audit trail for reads of historical data.

Rewrite References

Every Reference.reference in every resource has to be rewritten from Patient/123 to Patient/{new-uuid}. That is a data-migration pass over every resource in the store.

Missing references produce dangling links; the resolver returns 404 where the resource is actually still present under a new id. For the reference mechanic, [the difference between resource id and identifier[]](/uuid-toolkit/resource-id-vs-identifier-array/) is the entry.

Preserve The Old Id In identifier[]

While the migration is in flight and possibly after, put the old sequential id into identifier[] with an system URL that names your organization's legacy id namespace. Consumers that still lookup by the old id can search on the identifier.

This is not a permanent solution. It is a bridge. Retire the identifier once no consumer looks for it.

Migrate The Audit Log

Every audit log entry references a resource id. After migration, entries still reference the old id. That is fine for the audit trail but confusing for support.

Options:

  • Add a "migrated to" field on every audit entry
  • Keep an old-id-to-new-id lookup available at read time
  • Migrate audit entries too (expensive)

Pick one. Do not do all three.

Update Downstream Systems

Every downstream system that reads the old ids needs an update:

  • Reports that key on the id
  • Caches that store the id
  • Message queues that carry the id
  • Search indices that index the id

Do a full survey before starting. Missing one produces the "we thought we were done" moment three months later.

For the cross-environment side, keeping UUIDs stable across environments is the entry.

Verify Before Cutover

  • Complete id-map coverage — every resource has a mapping
  • Reference-rewrite completion — no dangling references
  • Test read paths work with the new ids
  • Test write paths generate UUIDs, not sequences

Verification is not glamorous. Skipping it is where the "why is production emitting 500s" incidents come from.

The Rollback Story

Every migration needs a rollback. For sequential-to-UUID:

  • Keep the source database untouched during the migration
  • Migrate to a new store that can be swapped in/out
  • Keep both stores warm during a staged cutover
  • Only decommission the old store after a defined stable period

That is expensive. It is worth it for a change that touches every reference in the graph.

Collision Check

Newly-generated UUIDs need to avoid conflict with any existing UUIDs in the store. If your old store used UUIDs for some resources and sequential for others, the check matters. For the multi-tenant case, collision-free UUIDs in a multi-tenant FHIR deployment covers the isolation.

The Short Version

Build the id map, rewrite references, preserve the old id in identifier[], migrate audit and downstream systems, verify before cutover, keep a rollback story. Migration is a controlled operation, not a one-off script.

Halftone-comic diagram of a sequential-to-UUID migration pipeline with id-map, reference rewrite, downstream survey, and rollback path, with dot pattern shading and hot-pink accents on cream paper

Sources