defaults
Initialize Go structs with default values using simple struct tags and reflection.
Repository Health
Technical Analysis
defaults is a small reflection-based Go library that populates struct fields with default values declared via a default struct tag. It supports nearly every Go type — scalars, slices, maps, nested and pointer structs, and type aliases like time.Duration — while preserving any non-zero values already set on the struct so defaults never clobber real data.
For values that can’t be expressed as a static tag, structs can implement the Setter interface to compute defaults dynamically at initialization time, and the library exposes a CanUpdate helper so those setters can safely check whether a field is still at its zero value before overwriting it.
What You Get
- Tag-driven defaults - Declare a default value per field with a
default:"..."struct tag instead of writing manual initialization code. - Deep recursive initialization - Nested structs, slices of structs, maps of structs, and pointer fields are all walked and initialized automatically.
- Non-destructive by design - Only zero-value fields are touched; any value already set on the struct before calling
Setis preserved. - Dynamic defaults via
Setter- Structs can implementSetDefaults()to compute a default at runtime (e.g. a random value) instead of a static tag. - Broad type coverage - All numeric kinds,
bool,string,time.Duration, aliased types, and anything implementingencoding.TextUnmarshaler/json.Unmarshalerare supported out of the box.
Common Use Cases
- Config struct initialization - Populate a configuration struct’s unset fields with sane defaults after unmarshaling partial YAML/JSON/env input.
- Test fixture construction - Build fully-populated struct instances for tests without repeating boilerplate default assignments in every test case.
- API request/response DTOs - Ensure optional fields on request structs have sensible defaults before validation or downstream processing.
- Library option structs - Give a package’s
Options/Configstruct baked-in defaults so callers only need to set the fields they care about.
Under The Hood
Architecture
The core lives in a single file (defaults.go) exposing Set/MustSet, which operates on a struct pointer via reflect. Field-level work is dispatched in setField, which first assigns scalar/container zero values based on the tag and reflect.Kind, then a trailing switch recurses into Ptr, Struct, Slice, and Map fields to handle nesting. The Setter interface (setter.go) is a small, isolated extension point invoked via callSetter after each Set/setField pass, letting types opt into runtime-computed defaults without touching the core recursion logic. There’s no dependency injection or external state — everything operates in place on the struct passed in, which keeps the design a tight, single-purpose monolith appropriate for its scope; the one blast-radius risk is that all consumers share the same Set entry point, so a signature change ripples to every caller.
Tech Stack
The module (github.com/creasty/defaults, Go 1.14) depends only on the standard library — reflect, encoding, encoding/json, errors, strconv, time — with zero third-party runtime dependencies, and the test suite likewise sticks to stdlib testing and reflect.DeepEqual rather than pulling in an assertion library. CI runs on CircleCI: go mod download, golangci-lint, make ci-test, and a Codecov coverage upload. Being a library rather than a service, there’s no build/deployment target beyond go build.
Code Quality
The test file is extensive (700+ lines) and exercises nearly every supported type — scalar variants, octal literals, aliased types, encoding.TextUnmarshaler via net.IP, JSON-unmarshaled structs, Setter interaction, pointers, and nested maps/slices — using straightforward stdlib assertions. Error handling is explicit and typed (a sentinel errInvalidType returned from Set, with a separate MustSet wrapper for callers who prefer a panic), and naming follows idiomatic Go conventions throughout. CI enforces golangci-lint and coverage reporting as automated quality gates beyond the tests themselves.
What Makes It Unique
The library’s distinguishing choice is pairing struct-tag defaults with a strict “zero value means unset” contract, enforced via reflect.DeepEqual against the type’s zero value, so Set never overwrites a field a caller has already populated — a guarantee simpler default-value helpers skip. It extends that same contract to dynamic defaults through the Setter interface and a CanUpdate helper, and special-cases encoding.TextUnmarshaler/json.Unmarshaler so tags can carry structured values without bespoke per-type parsing. These are meaningful refinements on a well-established Go pattern (tag-driven struct defaults via reflection) rather than a fundamentally new technique.
Used by 3 apps in this directory
Dolt
Databases · Data Engineering · Developer Tools
The SQL database you can branch, merge, diff, and clone — Git for your data, MySQL-compatible and ready for multi-agent AI workflows.
Hatchet
AI Development · Developer Tools · Automation
A Postgres-backed orchestration engine for background tasks, AI agents, and durable workflows that replaces Redis queues and multi-datastore durable execution platforms with a single self-hostable service.
SpiceDB
Security · Authentication · Databases
An open source, Google Zanzibar-inspired authorization database that models permissions as relationships and evaluates fine-grained access checks at massive scale with single-digit millisecond latency.