← All articles
Developer Guides

Use UUIDs for Records: Generation and Common Mistakes

Use UUIDs for records with the right version and storage format, and avoid treating IDs as secrets or guaranteed unique keys across systems.

A UUID is a 128-bit identifier designed to identify an object across systems with a very low chance of collision when generated correctly. It can be useful when records are created on multiple machines or when a database sequence should not be exposed in a public URL. A UUID is still an identifier, not a password or access-control rule.

Choose the identifier for the system you have

A sequential integer is compact and can be efficient for a single database. It also reveals ordering and approximate record counts when exposed. A UUID is longer but can be generated independently in distributed systems without first asking one central service for the next number.

The choice affects storage, indexing, URLs, logs, and operations. UUIDs can increase index size compared with compact integer keys, and random insertion patterns may affect database locality. Database behavior differs, so benchmark with the actual schema and workload rather than assuming one key type is universally faster.

Use UUIDs when decentralised generation, cross-system references, or hard-to-enumerate identifiers are useful. Use a simpler key when the data is local, compactness and ordered inserts matter, and exposing the sequence does not create a problem. A UUID does not make a public record private: authorization must still be checked for every request.

Understand the version and generation method

UUID formats have versions with different generation approaches. UUIDv4 is based on random or pseudorandom bits. UUIDv7 includes a timestamp component and random bits, which can help create roughly time-ordered identifiers. Other versions derive values from namespaces or other defined inputs. The format and privacy properties depend on the version.

Do not select a version only because its string looks familiar. Check the current UUID specification, RFC 9562, the generator’s guarantees, and the needs of your application. A timestamp-based version may reveal approximate creation time; a name-based version may reveal patterns in its namespace and input if designed carelessly.

Use a maintained library or platform API that implements the version you intend. In browsers and modern JavaScript runtimes, crypto.randomUUID() generates a UUID using a cryptographically secure random source in supported secure contexts. Check your runtime and deployment requirements. Do not build a generator from Math.random() and assume that its output is a suitable security-sensitive token.

A Toolsnix UUID Generator can generate sample identifiers. For production systems, prefer your runtime’s library so generation is reproducible in code and covered by your project’s dependency and security practices.

Store and transmit the value consistently

A UUID commonly appears as a hexadecimal string with hyphens, but an application may store it in a native 16-byte representation, binary column, or canonical text form. Choose one representation at the system boundary and validate it. Avoid silently accepting malformed or ambiguous strings.

Database drivers and languages can differ in byte ordering or string conversion. Test round-trips between the database, application, API serializer, and any downstream consumer. If a service emits uppercase in one place and lowercase in another, decide whether comparisons are normalized and ensure every component follows the same rule.

When exposing identifiers in URLs, use a stable canonical form and validate input before querying. Use parameterized database queries; do not concatenate identifiers into SQL. An identifier should select a candidate record, not grant access to it.

Do not treat a UUID as a secret

A random UUID may be difficult to guess, but its purpose is identification, not authentication. It can appear in logs, support tickets, browser history, analytics, and referrer data. If possessing the value grants access to a private file or action, it is functioning as a bearer token and needs the protection, expiration, and revocation properties of a credential.

For private records, authenticate the requester and check authorization on the server for each resource. Do not rely on the obscurity of the UUID. Avoid placing sensitive personal data inside custom identifiers; a UUID’s standard format does not hide information if you embed or accompany it with meaningful data.

Plan for collisions and lifecycle anyway

UUID collisions are extremely unlikely for correctly generated UUIDs with sufficient randomness, but a database uniqueness constraint is still the final guard against duplicates. Handle a collision by retrying safely and logging the event without exposing sensitive data. Never assume an identifier value is unique because it “looks random.”

Consider lifecycle details: should an identifier ever be reused? Usually permanent record IDs should not be recycled, because old references may still exist. For deleted records, decide whether the ID remains as a tombstone, is removed, or is replaced by a new resource. Document the behavior for imports, merges, and migrations.

For APIs, define the field as a string with a clear format and document whether it is case-insensitive. For events, include a separate timestamp if you need event time; do not infer ordering from UUIDs unless the chosen version and application contract support that purpose.

Common mistakes

  • Using a UUID as authorization. Always enforce access checks.
  • Generating IDs with a weak random source. Use a vetted library or platform API.
  • Mixing representations. Define text or binary storage and test round-trips.
  • Assuming all UUID versions reveal the same information. Check version-specific properties.
  • Using UUIDs as sortable timestamps without a contract. Store event time explicitly when it matters.
  • Skipping the unique constraint. Let the database enforce uniqueness even when collision probability is tiny.

Account for ordering and privacy

Random UUIDs do not naturally sort in creation order. If users need “newest first,” store a timestamp and sort by that field rather than relying on an ID’s lexical order. UUIDv7 is designed with a time-ordered component, but it still should not replace an explicit event timestamp when business meaning depends on time. Check the standard and your database’s implementation before using it for ordering.

Time-ordered identifiers may reveal approximate creation time. That can be acceptable for internal records and still be undesirable for public URLs or privacy-sensitive workflows. Random UUIDs reduce obvious sequence leakage but should not be treated as confidential. Consider what an observer can infer from repeated identifiers and associated activity.

Plan import, merge, and synchronization

When importing data from two systems, identifiers may collide because systems copied the same records or used separate namespaces. Decide how to reconcile before the merge: preserve a source-system ID, create a new internal ID, or maintain a mapping table. Do not silently overwrite a record because its UUID matches.

For synchronization, include an idempotency strategy. A stable identifier can help recognize the same record across retries, but only if all participating systems agree on what the identifier means. Include a source or tenant boundary where records from different domains must not be confused, and enforce the relationship in the database.

Test validation at system boundaries

Validate UUID syntax at API boundaries and return a clear client error for malformed values. Then apply authorization and lookup rules separately. A valid UUID can still refer to a nonexistent record or one the caller cannot access.

Test uppercase and lowercase input if your protocol treats them equivalently, plus missing, truncated, extra-character, and null values. Test how the database driver converts text to its binary form. If clients submit UUIDs in a URL, ensure the router handles percent encoding and reserved characters consistently.

A unique constraint protects against collisions at the database boundary. If insertion fails with a uniqueness violation, do not blindly retry every database error; distinguish a duplicate identifier from a connection failure or invalid data. Good error handling makes rare collision behavior safe without hiding unrelated faults.

Choose a version that matches the data model

Use the current UUID specification rather than an old code snippet copied from an outdated article. RFC 9562 defines modern UUID versions and their layouts. Libraries may support different subsets, so verify the runtime’s implementation and compatibility with the database, API clients, and downstream systems.

Version 4 is often selected when a random identifier is desired. Version 7 incorporates time-ordering information and may be useful where approximate chronological sorting helps. Name-based versions can produce repeatable values from a namespace and name, but require careful namespace handling. These choices have different information and generation properties; there is no version that fits every design.

Migration from another identifier

If you replace integer IDs with UUIDs, plan the migration rather than changing the column type in place without impact analysis. Update foreign keys, API schemas, caches, analytics joins, background jobs, and client assumptions. Large tables may require a staged migration with a new column, backfill, dual reads or writes, and a carefully timed constraint change.

Keep external IDs distinct from internal database keys if the two serve different purposes. An imported source ID might be unique only within a tenant or vendor. Store the namespace or source alongside it instead of assuming it is globally unique.

Avoid UUIDs as product semantics

UUIDs are poor substitutes for values that carry business meaning, such as invoice sequence numbers or human-readable ticket references. Those often need ordering, policy, or customer support behavior. You can use a UUID as an internal primary key and a separate formatted public reference for the user-facing workflow.

Do not derive business dates, tenant identity, or permission scope from the UUID. Put those values in explicit fields with validation and authorization. This keeps migrations and access reviews understandable.

Include identifiers in observability carefully

UUIDs are useful correlation values in logs because they can connect events across services. Avoid logging sensitive request payloads alongside them. If an identifier itself reveals a customer or resource relationship, apply the organization’s log access and retention controls.

A correlation ID and a record ID may serve different purposes. A request correlation ID identifies one workflow; a record UUID identifies a persistent object. Use distinct field names so operators do not mistake one for the other or assume a request identifier can retrieve a record.

For public support references, consider whether a separate short code is easier for people to communicate. Keep the UUID as the machine identifier if needed, but do not make users read or dictate a 36-character string during support calls.

FAQs

Are UUIDs guaranteed to be unique?

No finite identifier space can guarantee that independently generated values never collide. Proper generation makes collisions extremely unlikely for many use cases; retain a database uniqueness constraint and handle conflicts.

Is a UUID secure enough to use as a password reset token?

Do not choose a value solely because it is a UUID. Reset tokens need a reviewed generation method, limited lifetime, single-use behavior where appropriate, secure storage, and careful logging controls.

Should I use UUIDv4 or UUIDv7?

Choose based on privacy, ordering, compatibility, database behavior, and current standard guidance. UUIDv4 is random-oriented; UUIDv7 has time-ordering information. Benchmark and document the choice.

Can users guess sequential UUIDs?

A correctly generated random UUID is harder to enumerate than a small sequential ID, but that is not a substitute for access control. Assume identifiers can be discovered or shared.

Next step

Document the UUID version, representation, and authorization model before adding it to an API or database. Generate values with a maintained library and enforce uniqueness in the data store.