Zig is still before 1.0, so the language and standard library can change between releases. Code written for one Zig version may not compile on another version without edits.
Zig is still before 1.0, so the language and standard library can change between releases. Code written for one Zig version may not compile on another version without edits.
This appendix explains how to migrate carefully.
J.1 Know Your Current Version
Check your Zig version first.
zig versionExample output:
0.16.0Do not guess. Migration starts with the exact source version and target version.
J.2 Pin the Version Per Project
A project should say which Zig version it expects.
Common places:
| Place | Example |
|---|---|
| README | “Requires Zig 0.16.0” |
| CI config | Install exact Zig version |
| Build scripts | Check zig version |
| Developer docs | Document setup command |
This prevents one developer from building with 0.15 while another builds with 0.16.
J.3 Read the Release Notes
Before editing code, read the release notes for every version you cross.
Look for:
| Area | Why |
|---|---|
| Language changes | Syntax or semantic changes |
| Standard library changes | Renamed, moved, or removed APIs |
| Build system changes | build.zig changes |
| Compiler changes | New errors or stricter checks |
| Package manager changes | Dependency workflow changes |
Do not migrate by random trial and error only. Compiler errors help, but release notes tell you the design direction.
J.4 Start with a Clean Build
Before changing anything, confirm the old version builds.
zig build
zig test .If the old version already fails, migration becomes harder. Fix existing breakage first, or at least record it clearly.
J.5 Create a Migration Branch
Use version control.
git checkout -b migrate-zig-0-16Make small commits:
git add .
git commit -m "Update build.zig for Zig 0.16"Small commits let you isolate problems.
J.6 Upgrade One Layer at a Time
Do not rewrite the whole project at once.
A good order:
| Step | Work |
|---|---|
| 1 | Build system |
| 2 | Dependency declarations |
| 3 | Imports |
| 4 | Standard library API changes |
| 5 | Language errors |
| 6 | Tests |
| 7 | Cleanup and style |
This avoids mixing unrelated problems.
J.7 Fix build.zig First
If the build file fails, the rest of the project may not compile at all.
Run:
zig buildFix build errors before source errors.
Common build migration areas:
| Area | Typical change |
|---|---|
| Target options | API shape changes |
| Optimize options | API shape changes |
| Module creation | New module APIs |
| Dependency use | Package manager changes |
| Install steps | Build graph changes |
J.8 Expect Standard Library Renames
Zig’s standard library changes often.
A function may be:
| Change type | Example |
|---|---|
| Renamed | old name replaced by clearer name |
| Moved | API moved to another namespace |
| Removed | API no longer belongs in std |
| Reworked | same task, new design |
| Split | one broad API becomes several smaller APIs |
When a standard library call fails, inspect the new function signature.
J.9 Search Before Editing
Use search tools.
rg "std\.ArrayList"
rg "readFileAlloc"
rg "@Type"
rg "Thread\.Pool"Batch similar changes together.
Example:
rg "readToEndAlloc"Then update all file-reading code in one pass.
J.10 Let the Compiler Guide You
After each small group of edits, compile again.
zig buildFix the first meaningful error.
Do not chase every error at once. One wrong type can produce many follow-up errors.
J.11 Watch for Type Inference Changes
Newer Zig versions may infer slightly different types or require more explicit casts.
Example pattern:
const n = 10;If the compiler needs a specific type, write it:
const n: usize = 10;Explicit types are useful at API boundaries, indexes, protocol fields, and binary formats.
J.12 Replace Removed Builtins
Some builtins change between versions.
For Zig 0.16, @Type was replaced by more specific type-construction builtins.
Old style:
const T = @Type(info);Newer style uses more specific construction APIs.
The exact replacement depends on what type you are building: integer, struct, enum, union, pointer, array, or another kind of type.
J.13 Review I/O Code Carefully
I/O is one of the areas that changed significantly in Zig 0.16.
File reading, file writing, standard input, standard output, process arguments, and environment handling may need updates.
Do not just patch names. Check the new ownership and I/O model.
Ask:
| Question | Why |
|---|---|
Does this function now require std.Io? | I/O dependency is explicit |
| Does this function allocate? | Allocator must be passed |
| Who owns returned memory? | Cleanup may change |
| Is there a limit parameter? | Avoid accidental unbounded reads |
J.14 Review Allocator Use
Allocator APIs and container patterns may change.
Check:
| Pattern | What to inspect |
|---|---|
ArrayList | init/deinit style |
| Hash maps | managed vs unmanaged variants |
| Arena allocator | thread-safety and lifecycle |
| Custom allocators | interface changes |
| Test allocators | leak detection behavior |
If a container no longer stores an allocator, pass the allocator to operations that need memory.
J.15 Review Error Handling
New versions may make errors more precise.
A function that previously returned one error set may now return another.
This can affect code like:
fn load() MyError!void {
try std.fs.cwd().openFile("data.txt", .{});
}If the filesystem function returns errors not in MyError, Zig will complain.
Fix by:
| Fix | When |
|---|---|
| Widening the error set | Public API can expose more errors |
| Mapping errors manually | Public API should stay stable |
| Handling errors locally | Error can be resolved here |
J.16 Keep Public APIs Stable When Possible
Internal code can change freely. Public APIs need more care.
If you maintain a library, avoid forcing every user to understand your migration.
Example wrapper:
pub fn readConfig(allocator: std.mem.Allocator, path: []const u8) !Config {
// internal Zig-version-specific logic here
}Users call readConfig, not every low-level API directly.
J.17 Update Tests Before Refactoring
After the code compiles, run tests.
zig build testor:
zig test src/main.zigTests reveal semantic changes that compilation alone may miss.
Only refactor after tests are green or after failures are understood.
J.18 Use Compatibility Shims Sparingly
Sometimes you can hide version differences behind a small helper.
fn readWholeFile(...) ![]u8 {
// version-specific implementation
}This is useful if you support multiple Zig versions.
But too many compatibility shims make code harder to read. For applications, it is often better to support one exact Zig version.
J.19 Do Not Support Too Many Zig Versions
Before Zig 1.0, supporting many versions can be expensive.
For libraries, support a small range.
For applications, pin one version.
Practical choices:
| Project type | Recommendation |
|---|---|
| Personal project | Pin one Zig version |
| Production app | Pin one tested version |
| Library | Support current stable, maybe one previous |
| Teaching material | Use one version consistently |
J.20 Update CI
Your continuous integration should install the same Zig version you support.
CI should run:
zig version
zig fmt --check .
zig build
zig build testThis catches version drift early.
J.21 Run Formatter
After migration, run:
zig fmt .Formatting changes may happen between versions. Let the formatter normalize the code.
J.22 Recheck Examples and Documentation
Examples often break during migration.
Check:
| File | Why |
|---|---|
| README examples | First thing users copy |
| Tutorial snippets | Often compile-sensitive |
| API docs | May mention old names |
| Build commands | Flags may change |
| Comments | May describe old behavior |
Documentation is part of migration.
J.23 Recheck Benchmarks
A version migration can change performance.
Run benchmarks again.
Use a release mode:
zig build -Doptimize=ReleaseFastCompare before and after.
Do not assume a successful compile means identical performance.
J.24 Recheck Binary Size
Compiler and linker changes can affect binary size.
Measure again after migration.
ls -lh zig-out/bin/myappFor size-sensitive work, test ReleaseSmall.
zig build -Doptimize=ReleaseSmallJ.25 Recheck Cross Compilation
If your project supports multiple targets, build them.
zig build -Dtarget=x86_64-linux
zig build -Dtarget=aarch64-linux
zig build -Dtarget=x86_64-windows
zig build -Dtarget=aarch64-macosMigration bugs may appear only on some targets because ABI, libc, filesystem, and linker behavior differ.
J.26 Migration Checklist
Use this checklist:
| Step | Done |
|---|---|
| Check old Zig version | |
| Check target Zig version | |
| Read release notes | |
| Create migration branch | |
Fix build.zig | |
| Fix dependency declarations | |
| Fix standard library API calls | |
| Fix language-level errors | |
| Run formatter | |
| Run tests | |
| Run benchmarks if needed | |
| Test cross targets if needed | |
| Update README and docs | |
| Pin version in CI |
J.27 Practical Rule
Migrate Zig projects with small, boring steps.
Change one category at a time.
Compile often.
Read signatures.
Keep allocation, errors, and ownership visible.
Do not treat migration as only a search-and-replace task. Zig version changes often reflect design changes, especially before 1.0.