Skip to main content

CompactRef

Generate compact, human-facing references from ULIDs, UUIDs and other stable internal identifiers.

CompactRef is useful when an application keeps a full internal identifier but needs a shorter reference for users, support teams, documents or searches.

CompactRef generates compact references, not globally unique identifiers.

A short reference has fewer possible values than the identifier it is derived from, so two identifiers can produce the same reference. Keep the ULID or UUID as the primary key, put a unique constraint on the reference column, and use attempt to derive another one when that constraint rejects a write. Choosing a suffix length sizes the reference so this stays rare.

PyPI Version PyPI License PyPI Python Version PyPI Status PyPI Downloads

Installation

pip install compactref

Generate a reference from a ULID

from compactref import generate_reference

reference = generate_reference(
    "01J2H8NQPG6B5X8KGN97SX3R5C",
)

print(reference)

Possible output:

20260710482731

Add a prefix and separators

from compactref import generate_reference

reference = generate_reference(
    "01J2H8NQPG6B5X8KGN97SX3R5C",
    prefix="INC",
    separator="-",
)

print(reference)

Possible output:

INC-20260710-482731

Use a UUID

from uuid import uuid4

from compactref import generate_reference

internal_id = uuid4()
reference = generate_reference(internal_id)

Configure the suffix length

reference = generate_reference(
    "01J2H8NQPG6B5X8KGN97SX3R5C",
    suffix_length=8,
)

Possible output:

2026071048273164

Change the date format

The date_format argument accepts any datetime.strftime pattern. A finer-grained format also produces smaller collision buckets (see Choosing a suffix length).

reference = generate_reference(
    "01J2H8NQPG6B5X8KGN97SX3R5C",
    date_format="%Y%m%d-%H",
    separator="-",
    prefix="INC",
)

Possible output:

INC-20260710-14-482731

Use an integer or bytes identifier

from compactref import generate_reference

from_integer = generate_reference(123456789)
from_bytes = generate_reference(b"internal-record-123")

Deterministic generation

The same identifier, date and configuration produce the same reference:

from datetime import datetime

from compactref import generate_reference

generated_at = datetime(2026, 7, 10)

first = generate_reference(
    "01J2H8NQPG6B5X8KGN97SX3R5C",
    generated_at=generated_at,
)

second = generate_reference(
    "01J2H8NQPG6B5X8KGN97SX3R5C",
    generated_at=generated_at,
)

assert first == second

Recovering from a collision

Because a reference is derived from its source, the same source always produces the same reference. Retrying a rejected reference therefore returns the identical string, however many times you ask.

attempt is what makes a unique constraint recoverable. Raising it derives a different reference from the same source, so when attempt 0 is already taken you can offer attempt 1:

from compactref import generate_reference

def assign_reference(session, product):
    for attempt in range(10):
        reference = generate_reference(
            product.id,
            prefix="RDR",
            separator="-",
            attempt=attempt,
        )
        if not session.query(exists_reference(reference)).scalar():
            return reference

    raise RuntimeError("ten attempts collided; the suffix is too short")

Each attempt is deterministic in its own right, so a reference remains recomputable later from the source and the attempt that won — store the attempt alongside the reference if you need to rederive it.

first = generate_reference("01J2H8NQPG6B5X8KGN97SX3R5C")
second = generate_reference("01J2H8NQPG6B5X8KGN97SX3R5C", attempt=1)

assert first != second
assert second == generate_reference(
    "01J2H8NQPG6B5X8KGN97SX3R5C",
    attempt=1,
)

attempt defaults to 0, which reproduces the references CompactRef produced before the argument existed. References already stored by callers on 0.1.0 remain valid.

Reaching for attempt on most writes is a sign the suffix is too short, not that the retry loop is working. Size it with expected_collisions() below.

Supported source types

CompactRef accepts:

  • ULIDs represented as strings
  • UUID objects
  • strings
  • bytes
  • non-negative integers

Choosing a suffix length

A reference is unique only within a single bucket — references that share the same prefix and date part. Because the date resets each day, what matters is how many references you expect per bucket (for the default format, per day), not the all-time total.

Two helpers size the suffix using the birthday model.

Estimate the collision risk

collision_probability(reference_count, suffix_length) returns the probability that at least two references in one bucket share the same suffix:

from compactref import collision_probability

collision_probability(50, suffix_length=4)   # 0.1153  -> ~11.5%
collision_probability(50, suffix_length=6)   # 0.0012  -> ~0.1%
collision_probability(120, suffix_length=4)  # 0.5103  -> coin flip

Count the collisions, not just the risk

collision_probability() saturates. Past a certain volume every format reports "almost certainly", which stops separating a format that collides twice a month from one that collides fifty times.

expected_collisions(reference_count, suffix_length) returns how many colliding pairs are expected in one bucket — two references sharing a suffix is one pair:

from compactref import collision_probability, expected_collisions

collision_probability(2_000, suffix_length=3)   # 1.0   -> "certain"
collision_probability(20_000, suffix_length=3)  # 1.0   -> "certain", equally

expected_collisions(2_000, suffix_length=3)     # 1999   pairs
expected_collisions(20_000, suffix_length=3)    # 199990 pairs

Both formats are certain to collide. Only the second number says how badly, which is what sizes a suffix.

It is a measure of crowding, not a count of retries.

A colliding pair is not a rejected insert. A suffix drawn k times is k * (k - 1) / 2 pairs but only k - 1 rejected inserts, so the two agree while a bucket is sparse and part company once it fills. Two thousand references over three digits is 1999 pairs but roughly 1135 rejected inserts — the pair count overstates the retries by more than half.

Use it to compare formats. Do not size a retry budget with it.

Find a safe volume

max_references(suffix_length, max_probability=0.01) returns the largest number of references that keeps the risk at or below the threshold (1% by default):

from compactref import max_references

max_references(4)         # 14   -> under 1% risk with 4 digits
max_references(6)         # 142  -> under 1% risk with 6 digits
max_references(6, 0.05)   # 320  -> if you accept up to 5% risk

Pick a length for your volume

from compactref import collision_probability

expected_per_day = 200

for length in range(4, 9):
    risk = collision_probability(expected_per_day, suffix_length=length)
    print(f"{length} digits -> {risk:.3%}")

# 4 digits -> 86.330%
# 5 digits -> 18.045%
# 6 digits -> 1.970%
# 7 digits -> 0.199%
# 8 digits -> 0.020%

For roughly 200 references per day, a 7-digit suffix keeps the risk well under 1%.

Uniqueness warning

CompactRef does not replace the original internal identifier.

Shortening an identifier reduces the number of possible values. Different internal identifiers can produce the same compact reference. No suffix length makes this impossible; a longer one only makes it rarer.

Applications requiring unique references should:

  1. Keep the original ULID or UUID as the internal identifier. The reference is for humans; the identifier is for the database.
  2. Add a unique constraint to the reference column, so a collision surfaces as a rejected write rather than two products quietly sharing a reference.
  3. Handle that rejection by retrying with a higher attempt, as in Recovering from a collision.
  4. Size suffix_length for the expected volume per bucket, using expected_collisions(), so step 3 stays a rare path rather than the normal one.

Requirements

Python 3.10 or newer. No runtime dependencies.

Changelog

See CHANGELOG.md.

Version 0.2.0 added the attempt argument and expected_collisions(). References produced by 0.1.0 are unchanged: attempt defaults to 0, which reproduces them byte for byte, so anything already stored stays valid.

Contributing

Issues and pull requests are welcome at github.com/neosergio/compactref.

Maintainers: see RELEASING.md for how a version reaches PyPI.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

compactref-0.2.1.tar.gz (15.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

compactref-0.2.1-py3-none-any.whl (9.5 kB view details)

Uploaded Python 3

File details

Details for the file compactref-0.2.1.tar.gz.

File metadata

  • Download URL: compactref-0.2.1.tar.gz
  • Upload date:
  • Size: 15.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for compactref-0.2.1.tar.gz
Algorithm Hash digest
SHA256 447e8595533b86b57946627dfdd79ed2a80a34b081609a86ed408295cb6f7f54
MD5 611067a898bc7948e2bcc049622a0ea6
BLAKE2b-256 faf96744a3efd4b84bfa6720206c944561774493326583a94fd8080ee2738649

See more details on using hashes here.

Provenance

The following attestation bundles were made for compactref-0.2.1.tar.gz:

Publisher: publish.yml on neosergio/compactref

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file compactref-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: compactref-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 9.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for compactref-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 5a30143eee29656bb52846a446086e8c0c0fe79b80693e218059fab116c8edcf
MD5 3d313c7c223e937fc9f521dfff1cf110
BLAKE2b-256 460853228d656857cf1e5771601f70a27b949da0b9439ca19cfbe436f8b4b391

See more details on using hashes here.

Provenance

The following attestation bundles were made for compactref-0.2.1-py3-none-any.whl:

Publisher: publish.yml on neosergio/compactref

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

1.0.0

2 files

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

This release

0.2.1 This release

2 files

0.2.0

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page