Stop mixing up your IDs.
Phantom types prevent mixing entity IDs at compile time. Zero-allocation, stdlib-only, with full serialization for JSON, SQL, Text, Binary, and Gob.
package main
import (
"fmt"
"github.com/larsartmann/go-branded-id"
)
type UserBrand struct{}
func (UserBrand) Name() string { return "User" }
type OrderBrand struct{}
type UserID = id.ID[UserBrand, string]
type OrderID = id.ID[OrderBrand, string]
func GetUser(id UserID) error { return nil }
func GetOrder(id OrderID) error { return nil }
func main() {
userID := id.NewID[UserBrand]("user-123")
fmt.Println(userID) // User:user-123
// GetOrder(userID) // COMPILE ERROR
GetUser(userID) // OK
} Type safety without the cost.
Every feature designed for zero runtime overhead and seamless integration with existing Go code.
Compile-Time Type Safety
Phantom types make it impossible to pass a UserID where an OrderID is expected. The compiler catches the bug before your code runs.
Zero Allocations
Core operations (NewID, Get, Equal, Compare, IsZero) allocate nothing. NewID runs in 0.4 nanoseconds. No GC pressure.
Full Serialization
JSON, SQL, Text (XML/TOML), Binary, and Gob all implemented. Zero values serialize to null. Works with database/sql out of the box.
Named Brands
Optional Name() method enables debug-visible IDs like User:abc123 and brand-aware validation errors. Opt-in, zero cost if unused.
Any Comparable Type
ID[Brand, V comparable] works with strings, ints, and any comparable type. Full serialization for 12 built-in numeric and string types.
Stdlib-Only
No third-party dependencies. Uses encoding/json/v2 from the Go standard library. Nothing to audit, nothing to break.
Phantom types. Real safety.
Brand types are empty structs that exist only in the type system. Zero runtime cost, total compile-time protection.
Define Brand
Create an empty struct as a phantom brand type.
type UserBrand struct{} Create ID Type
Alias ID with your brand and value type.
type UserID = id.ID[UserBrand, string] Use in Functions
Function signatures enforce the brand at compile time.
func GetUser(id UserID) error Compile-Time Safety
Passing the wrong ID type is a compile error, not a runtime bug.
GetOrder(userID) // won't compile Plain types vs branded types.
Same performance, same dependencies. The only difference is the compiler now has your back.
| Plain types | go-branded-id | |
|---|---|---|
| Compile-time type safety | ||
| Zero-allocation core ops | ||
| Debug-visible IDs | ||
| JSON serialization | manual | built-in |
| SQL Scan / Value | manual | built-in |
| Brand-aware validation | ||
| Third-party dependencies | 0 | 0 |
Built for real Go projects.
Drop branded IDs into any layer where mixing entity types would cause bugs.
REST APIs
Route params and request bodies carry typed IDs that can't be mixed
Database Models
Scan directly from SQL rows. Zero values serialize to null
Domain-Driven Design
Strong domain types that make impossible states unrepresentable
Stop mixing up your IDs.
One import. Total type safety.