Zig Best Practices
Follows type-first, functional, and error handling patterns from CLAUDE.md. This skill covers Zig-specific idioms only.
Type System Patterns
Tagged unions for mutually exclusive states — prevents invalid combinations that a struct with multiple nullable fields would allow:
zigconst RequestState = union(enum) { idle, loading, success: []const u8, failure: anyerror, };
Explicit error sets — documents exactly what can fail; anyerror hides failure modes:
zigconst ParseError = error{ InvalidSyntax, UnexpectedToken, EndOfInput }; fn parse(input: []const u8) ParseError!Ast { ... }
Distinct types for domain IDs — compiler prevents mixing up different ID types:
zigconst UserId = enum(u64) { _ }; const OrderId = enum(u64) { _ };
Comptime validation — catch invalid configurations at compile time, not runtime:
zigfn Buffer(comptime size: usize) type { if (size == 0) @compileError("buffer size must be greater than 0"); return struct { data: [size]u8 = undefined, len: usize = 0 }; }
Memory Management
- Pass allocators explicitly to every function that allocates; no global allocator state.
- Place
defer resource.deinit()immediately after acquisition — keeps cleanup co-located with creation. - Use
errdeferfor cleanup on error paths;deferfor unconditional cleanup. - Use arena allocators for batch/temporary work; they free everything at once.
- Use
std.testing.allocatorin tests — reports leaks with stack traces.
zigfn createResource(allocator: std.mem.Allocator) !*Resource { const resource = try allocator.create(Resource); errdefer allocator.destroy(resource); // runs only on error resource.* = try initializeResource(); return resource; }
Key Conventions
- Prefer
constovervar; prefer slices over raw pointers. - Prefer
comptime T: typeoveranytype; explicit types produce clearer errors. Useanytypeonly for genuinely polymorphic cases (callbacks,std.debug.print-style). - Exhaustive
switch: include anelsereturning an error orunreachablefor truly impossible cases. - Use
std.log.scoped(.module_name)for namespaced logging; define a module-levelconst logconstant. - Larger cohesive files are idiomatic — tests alongside implementation, comptime generics at file scope.
Advanced Topics
- Generic containers (queues, stacks, trees): See GENERICS.md
- C library interop (raylib, SDL, curl): See C-INTEROP.md
- Debugging memory leaks (GPA, stack traces): See DEBUGGING.md
Tooling
zigdoc — browse std library and dependency docs:
bashzigdoc std.mem.Allocator # std lib symbol zigdoc vaxis.Window # project dependency zigdoc @init # create AGENTS.md with API patterns
ziglint — static analysis with .ziglint.zon config:
bashziglint # lint current directory ziglint --ignore Z001 # suppress specific rule
References
- Language Reference: https://ziglang.org/documentation/0.15.2/
- Standard Library: https://ziglang.org/documentation/0.15.2/std/
- Zig Guide: https://zig.guide/

