Status: Accepted
Date: 2026-03-28
Supersedes: N/A
Superseded by: N/A
Context#
ztick is a time-based job scheduler that needs:
- Fast compilation — Developers need tight feedback loops; CI must stay fast
- Minimal dependencies — Reduce supply chain risk; audit the entire stack
- Runtime simplicity — Single-threaded core with 3 helper threads; no need for complex async runtimes
- Explicit memory control — Scheduler needs predictable, deterministic allocation
- Small deployment — Binary should be self-contained, deployable to minimal environments
Candidates#
| Option | Pros | Cons |
|---|
| Zig | Blazing fast compile (2-3 sec); stdlib only; explicit allocators; minimal binary | Less mature; smaller community; no garbage collection |
| Rust | Mature ecosystem; strong type system; large community | Slow compile (30+ sec for similar projects); heavy dependency graphs; overkill async ecosystem for this domain |
| C | Extremely fast; minimal binary; portable | No type safety; manual memory is error-prone; no modern tooling |
| Go | Fast compile; solid stdlib; deployment story | Garbage collection (unpredictable latency); heavier binary |
Decision#
Build ztick in Zig (currently 0.15.2).
The scheduler’s requirements align perfectly with Zig’s strengths:
- Zero dependencies: ztick uses only
std — no external packages needed - Explicit allocator passing: The scheduler needs predictable allocation; Zig forces this via function parameters
- Comptime for correctness: Use
comptime for protocol validation, type dispatching - Build speed: 2-3 seconds for a full build in CI
- Binary size: ~3MB static binary, trivially deployable
Consequences#
What becomes easier:
- Compile time for developers (2-3 seconds)
- Auditing dependencies (none; only Zig stdlib)
- Understanding memory behavior (all allocations explicit)
- Deploying to constrained environments (binary ~3MB static)
- Adding features without bloating a dependency graph
What becomes harder:
- Recruiting developers (fewer Zig developers in industry)
- Reusing library code from larger ecosystems
- Async I/O patterns (must be threaded instead)
- Debugging runtime issues (smaller community, fewer resources)
Implementation Notes#
Key design decisions:
- i64 nanosecond timestamps (matches binary format, no datetime library needed)
- Hand-written minimal TOML parser (config is simple: 3 sections, 4 values)
- Custom bounded channel over
std.event (0.14.1 async unstable) - Shell runner only
Constitution Compliance#
| Principle | Status | Justification |
|---|
| Zig Idioms (error unions, comptime, std.log) | Compliant | Error unions throughout; no @panic() in library code |
| Minimal Abstraction | Compliant | No external packages; stdlib only; language provides necessary primitives |
| No unsafe runtime | Compliant | Zig’s memory safety model with explicit allocators prevents undefined behavior |
| Build simplicity | Compliant | Single build.zig file; no build scripts or external tools |
Trade-offs Accepted#
- Zig immaturity: 0.14.1 is recent. Mitigated by: pinning version in CI, stable stdlib APIs, co-located tests for regression detection
- Smaller ecosystem: Zig has fewer libraries. Mitigated by: ztick needs no external deps; protocol parser/TOML parser hand-written per specification
- Community size: Zig community is smaller. Mitigated by: architecture is well-documented; code is self-explanatory; contribution guidelines clear
References#
- Architecture:
docs/development/architecture.md - Build Guide:
docs/development/building.md - Zig Docs: https://ziglang.org/documentation/