Skip to content
Branded IDs for Go

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.

0
Allocations
0
Dependencies
~0.4
ns NewID
MIT
License
main.go
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
}
Features

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.

How it works

Phantom types. Real safety.

Brand types are empty structs that exist only in the type system. Zero runtime cost, total compile-time protection.

1

Define Brand

Create an empty struct as a phantom brand type.

type UserBrand struct{}
2

Create ID Type

Alias ID with your brand and value type.

type UserID = id.ID[UserBrand, string]
3

Use in Functions

Function signatures enforce the brand at compile time.

func GetUser(id UserID) error
4

Compile-Time Safety

Passing the wrong ID type is a compile error, not a runtime bug.

GetOrder(userID) // won't compile
Comparison

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
Use Cases

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

Get Started

Stop mixing up your IDs.

One import. Total type safety.