Generating good unique IDs in Go

Generating good unique IDs in Go

part of Go Cookbook
Each note in a note-taking application needs an ID. If every write goes through a database, an auto-incrementing key may be enough. If clients create notes offline or several services generate IDs independently, we need another strategy.
Random IDs avoid coordination, but collisions are improbable rather than impossible. Keep a uniqueness constraint where IDs are stored and handle a collision if one occurs.

UUIDs in the standard library

Go 1.27 added the uuid package. It supports random version 4 UUIDs and time-ordered version 7 UUIDs, standardized in RFC 9562.
This complete example requires Go 1.27 or later:
package main

import (
	"fmt"
	"uuid"
)

func main() {
	fmt.Println("random:", uuid.NewV4())
	fmt.Println("time-ordered:", uuid.NewV7())
}
Version 4 contains 122 random bits; the remaining bits identify the format. Version 7 includes a timestamp, making it useful when keys should cluster by creation time. Do not interpret that order as a global event ordering across machines: clocks can differ or move backward.
On earlier Go releases, github.com/google/uuid supports both formats:
package main

import (
	"fmt"
	"log"

	"github.com/google/uuid"
)

func main() {
	randomID, err := uuid.NewRandom()
	if err != nil {
		log.Fatal(err)
	}
	orderedID, err := uuid.NewV7()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(randomID)
	fmt.Println(orderedID)
}
Add that dependency with go get github.com/google/uuid. Its API is different from the standard library’s: for example, its NewV7 returns an error as well as the ID.

Shorter sortable formats

UUIDs are not the only choice. These formats have different tradeoffs:
Format Text length Main properties
UUID 36 Standard format; choose v4 for random IDs or v7 for time clustering
ULID 26 Millisecond timestamp and randomness; sortable Base32 text
KSUID 27 Timestamp and randomness; sortable Base62 text
XID 20 Timestamp, machine/process information, and counter; compact Base32 text
This is a survey of formats, not a guarantee that every format is suitable for every deployment. XID and Snowflake-style generators have different machine-identity and coordination assumptions from a random UUID. Time-based formats also disclose approximate creation time.
For ULIDs, use a cryptographic entropy source rather than constructing a new time-seeded math/rand generator for each ID:
package main

import (
	"crypto/rand"
	"fmt"
	"log"
	"time"

	"github.com/oklog/ulid/v2"
)

func main() {
	id, err := ulid.New(ulid.Timestamp(time.Now()), rand.Reader)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(id)
}
Install with go get github.com/oklog/ulid/v2. Random ULIDs created in the same millisecond do not necessarily sort in generation order; use the library’s monotonic entropy facility if that property is required, and follow its concurrency contract.
The site’s ID comparison page shows several of the libraries from the original survey. Its set of generators differs from the examples here.

IDs and secret tokens serve different purposes

An ID identifies an object. Access control must still decide who can read or change it. A sortable ID, especially one with timestamps or counters, should not be treated as an authentication secret.
For an opaque random token, Go 1.24 added crypto/rand.Text:
token := rand.Text() // import "crypto/rand"
It returns a URL-safe Base32 string with at least 128 bits of randomness. Its length may increase in future releases, so do not hard-code the current length in a storage schema.
For new code I would start with UUID v4 or v7, then choose a shorter format only if its size or sorting behavior solves a concrete requirement. The original example code records the libraries used in the 2017 survey.
#go
Sep 5 2026

Related articles

Feedback about page:

Feedback:
Optional: your email if you want me to get back to you: