Using Bitmasks for Category Indexing Inside a Reference ID
The cleaner way to pack multiple categories into a fixed-length ID

If you've ever had to design a reference ID system, the kind where the format is fixed, the length is strict, and every character counts, you've probably run into this problem:
"I need to store multiple category flags in here. How do I do that cleanly?"
Most developers reach for a comma-separated string like "A,C,D" or just store a plain number like 3. Both work, until they don't. In this article I'll show you a cleaner, more elegant approach using bitmasks, and how to apply it directly inside a structured reference ID.
The Problem
Let's say you have a reference ID that's exactly 20 characters long. The first 15 characters are fixed, given to you by the system, and you can't touch them. The remaining 5 characters are yours to work with.
[ F I X E D _ P A R T _ 1 2 3 4 5 ] [ ? ? ? ? ? ]
|_______________________________| |_________|
First 15 chars Your 5 chars
Now you need those last 5 characters to represent which categories are active for that record. You have 4 categories — A, B, C, and D — and any combination of them can be active at the same time.
So how do you store that?
The Naive Approaches (And Why They Hurt)
Option 1 — Store a string like "A,C"
REF: FIXEDPART123456A,C
Now what if both A and C are active? You store 1 + 3 = 4. But wait, 4 is also just category D. You've lost the ability to tell combinations apart. Ambiguous and broken.
Enter Bitmasks
Here's the key insight: instead of assigning sequential numbers to categories, you assign each category a power of 2.
Category A = 2⁰ = 1 → 0001
Category B = 2¹ = 2 → 0010
Category C = 2² = 4 → 0100
Category D = 2³ = 8 → 1000
Each category occupies its own unique bit position. No two categories share a bit. This means every possible combination of active categories produces a completely unique number, no ambiguity, ever!
A only = 0001 = 1
B only = 0010 = 2
A + B = 0011 = 3
C only = 0100 = 4
A + C = 0101 = 5
A + B + C + D = 1111 = 15
See how every combination maps to a different value? That's the magic.
Applying It to the Reference ID
With 4 binary digits, we can represent all combinations of our 4 categories. That's exactly 4 characters — well within our 5-character budget.
REF ID: FIXEDPART123456 + 0101
||||
|||└─ Category A: active (1)
||└── Category B: inactive (0)
|└─── Category C: active (1)
└──── Category D: inactive (0)
Clean, fixed length, and completely readable if you know the system.
Code Implementation in Go
Let's make this real. Here's how you'd build and read these reference IDs in Go.
package main
import (
"fmt"
"strconv"
"strings"
)
// Define category bitmask values
const (
CategoryA = 1 << iota // 2⁰ = 1
CategoryB // 2¹ = 2
CategoryC // 2² = 4
CategoryD // 2³ = 8
)
// BuildRefID encodes active categories into a reference ID
func BuildRefID(fixedPart string, categories ...int) string {
bitmask := 0
for _, cat := range categories {
bitmask |= cat
}
bits := strconv.FormatInt(int64(bitmask), 2)
padded := fmt.Sprintf("%04s", bits)
padded = strings.ReplaceAll(padded, " ", "0")
return fixedPart + padded
}
// GetCategories decodes which categories are active from a reference ID
func GetCategories(refID string) []string {
bitStr := refID[len(refID)-4:]
val, _ := strconv.ParseInt(bitStr, 2, 64)
active := []string{}
if val&CategoryA != 0 {
active = append(active, "A")
}
if val&CategoryB != 0 {
active = append(active, "B")
}
if val&CategoryC != 0 {
active = append(active, "C")
}
if val&CategoryD != 0 {
active = append(active, "D")
}
return active
}
func main() {
fixedPart := "FIXEDPART123456"
// Build an ID with categories A and C active
refID := BuildRefID(fixedPart, CategoryA, CategoryC)
fmt.Println("Reference ID:", refID)
// Output: FIXEDPART1234560101
// Decode it back
active := GetCategories(refID)
fmt.Println("Active categories:", active)
// Output: [A C]
}
Notice the use of 1 << iota in Go — this is idiomatic Go for defining bitmask constants. It automatically shifts bits to the left for each constant, giving us our powers of 2 cleanly.
You've Seen This Before
Bitmasks aren't a niche trick — they're everywhere once you know what to look for:
Unix file permissions — when you run chmod 755, that 7 is 4+2+1 : read, write, and execute bits combined into one number.
Feature flags — many SaaS systems store which features a user has access to as a single bitmask integer in the database.
Game development — collision layers, input states, and entity flags all commonly use bitmasks to track multiple states efficiently.
When to Use This (And When Not To)
Use bitmasks when:
Your categories are independent of each other
You need a fixed-length representation
You want fast bitwise checks instead of string comparisons
You have a reasonable number of categories (up to ~30 comfortably)
Think twice when:
Categories have dependencies or hierarchy between them
Non-technical people need to read the ID manually
You have more than 30–40 categories (consider a different data structure)
Wrapping Up
Bitmasks are one of those tools that feel almost too simple once you get them but the impact on system design is real. A fixed length reference ID that cleanly encodes multiple category states, with no ambiguity and no wasted space, is something you'll appreciate every time you have to debug or extend it.
Next time you're designing a reference ID or need to store multiple boolean flags efficiently, give bitmasks a shot. Your future self will thank you.
If this helped you, drop a reaction or share it with a fellow dev. And if you've used bitmasks in a creative way in your own systems, I'd love to hear about it in the comments.
