Skip to main content
Beads uses adaptive hash ID lengths that automatically scale based on database size, optimizing for readability in small databases while preventing collisions as databases grow.

Motivation

  • Small databases (0-500 issues): Very short, readable IDs like bd-a3f2 (4 chars)
  • Medium databases (500-1500 issues): Slightly longer IDs like bd-7f3a8 (5 chars)
  • Large databases (1500+ issues): Standard IDs like bd-7f3a86 (6 chars)
Users who actively archive old issues can keep their IDs shorter over time.

How It Works

Birthday Paradox Math

The collision probability is calculated using:
Where:
  • n = number of issues in database
  • N = total possible IDs (36^length for lowercase alphanumeric)

Default Thresholds (25% max collision)

Collision Resolution

If a collision occurs (rare), the algorithm automatically tries:
  1. Base length (e.g., 4 chars)
  2. Base + 1 (e.g., 5 chars)
  3. Base + 2 (e.g., 6 chars)
With 10 nonces per length, giving 30 attempts total.

Configuration

Adaptive ID length is automatically enabled when using id_mode=hash. You can customize the behavior:

Max Collision Probability

Default: 25% (0.25)

Minimum Hash Length

Default: 4 chars

Maximum Hash Length

Default: 8 chars

Examples

Default Configuration

Custom Configuration

Collision Probability Table

Use scripts/collision-calculator.go to explore collision probabilities:
Output shows:
  • Collision probabilities for different database sizes and ID lengths
  • Recommended ID lengths for different thresholds
  • Expected number of collisions
  • Adaptive scaling strategy

Implementation Details

Location

  • Algorithm: internal/storage/dolt/adaptive_length.go
  • ID generation: internal/storage/dolt/dolt.go (generateHashID)
  • Tests: internal/storage/dolt/adaptive_length_test.go
  • E2E tests: internal/storage/dolt/adaptive_e2e_test.go

Database Schema

Configuration is stored in the config table:

Performance

  • Collision probability calculation: ~10ns per call
  • ID generation with adaptive length: ~300ns (same as before)
  • Database query to count issues: ~100μs

Migration

Existing Databases

Existing databases with 6-char IDs will:
  1. Continue using 6-char IDs by default
  2. Can opt into adaptive mode by setting config (new IDs will use adaptive length)
  3. Old IDs remain unchanged

Sequential to Hash Migration

When migrating from sequential IDs to hash IDs with bd migrate --to-hash-ids:
  • Uses adaptive length algorithm for new IDs
  • Preserves existing sequential IDs
  • References are automatically updated

Best Practices

  1. Default is good: The 25% threshold works well for most use cases
  2. Active archival: Delete closed issues to keep database small and IDs short
  3. Consistency: Set min_hash_length if you want all IDs to be same length
  4. Monitoring: Run collision calculator periodically to check health

Future Enhancements

Potential improvements (not yet implemented):
  • Automatic scaling notifications: Warn when approaching threshold
  • Per-workspace thresholds: Different configs for different projects
  • Dynamic adjustment: Auto-adjust threshold based on observed collision rate
  • Compaction-aware: Don’t count compacted issues in collision calculation

Alternative: Sequential Counter IDs

Adaptive hash IDs are the default, but beads also supports sequential integer IDs (bd-1, bd-2, …) for projects that prefer human-readable numbering. Counter mode is controlled by the issue_id_mode config key:
Tradeoff:
  • Hash IDs (this document): Collision-free across parallel branches and agents; IDs are less predictable but always unique.
  • Counter IDs: Human-friendly and sequential; require care in multi-branch workflows where counters can diverge.
See Configuration for full documentation on issue_id_mode=counter, including migration guidance and per-prefix counter isolation.