Skip to content
Published on

Modern Zig 2026 — Zig 0.14, Andrew Kelley, Bun, TigerBeetle, Ghostty, comptime, Zon, Roc Deep Dive

Authors

1. Zig in 2026 — Andrew Kelley vision and industrial adoption

As of May 2026, Zig is still not 1.0. The latest release is 0.14 from March 2025. But do not be fooled by the "0.x" label. Serious products like Bun, TigerBeetle, and Ghostty are already shipping in Zig, and thousands of C/C++ projects swap out their cross-compiler with a single zig cc invocation.

Zig core identity is one solo BDFL — Andrew Kelley — backed by the Zig Software Foundation. The opposite of Rust transitioning from Mozilla to a foundation and distributing governance: Zig is intentionally one person taste deeply etched into a language. The consequences:

  • A C-like simple surface, paired with comptime — powerful compile-time meta-programming
  • No hidden control flow — no operator overloading, no RAII, no hidden allocator
  • Explicit memory management, allocator passed as a function argument
  • zig cc / zig c++ — Zig bundles LLVM and works as a serious C/C++ cross-compiler
  • build.zig — replaces Make/CMake/Bazel; the build graph is written in Zig itself

Industrial adoption is "still small but very serious". Bun share of npm traffic, TigerBeetle traction in fintech ledgers, Ghostty quietly eating macOS-developer terminal share — those three alone prove Zig is not a toy.

This article starts from Andrew Kelley design philosophy, then walks through what 0.14 actually delivers, the 0.15 / 1.0 roadmap, and Bun / TigerBeetle / Ghostty before getting into comptime, allocators, the build system, zig cc, ZLS, and Roc — the reasons (and traps) for taking Zig seriously in May 2026.


2. Zig 0.14 (March 2025) — compilation speed and async on hold

Zig 0.14 shipped on March 5, 2025. Key changes:

  1. Progress on the native x86 backend — work to skip LLVM for debug builds. Reports of debug compile times nearly halving in some projects.
  2. Simpler @import dependency tree — the module system feels more intuitive.
  3. std.Build (build system) API churnbuild.zig taste shifted again. Migrating from 0.13 to 0.14 means walking through signature changes like step.addArgs.
  4. async / await still removed — async features pulled out in 0.11 still are not back in 0.14. Andrew said "we will redesign the stackless coroutine model", and whether that redesign lands before 1.0 is the big open question.

Code-level feel of 0.14:

const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    var list = std.ArrayList(u8).init(allocator);
    defer list.deinit();

    try list.appendSlice("Hello, Zig 0.14!\n");
    try std.io.getStdOut().writeAll(list.items);
}
  • try propagates errors; defer runs on scope exit
  • You construct std.heap.GeneralPurposeAllocator directly and extract the interface via allocator()
  • Every collection takes an allocator — no hidden alloc

The big-picture story of 0.14: the compiler is making itself faster. Exactly the opposite vector from Rust catching flak for rustc compile times.


3. Zig 0.15 / Zig 1.0 roadmap

As of May 2026, 0.15 has not shipped yet. Things expected or being discussed between 0.14 and 0.15:

  • Native x86 backend as default — bypassing LLVM in debug builds becomes the default. Release builds still go through LLVM.
  • Async/await redesign — Andrew proposed a new "I/O-driven concurrency" model. The core idea: not stackless coroutines, but async via an explicit io argument. Roughly fn read(io: *Io, ...) []u8. A library opts into async only when it consents.
  • std.Io — the standard interface for async I/O. Still in flux.
  • Stronger incremental compilation
  • Package manager (Zon) stabilizationbuild.zig.zon became the standard in 0.14; 0.15 polishes it further

When will Zig 1.0 land? Nobody can pin it down. Andrew officially says "1.0 ships when we can credibly promise stability". Conservative read: 2027. Optimistic: late 2026. Serious users like Bun / TigerBeetle / Ghostty are already shipping on 0.x — they are not waiting for 1.0; they migrate their code with every release.


4. Bun runtime — the biggest validation of Zig

Bun is a JavaScript runtime built by Jarred Sumner. Its core positioning is "Node.js replacement", and the engine is written almost entirely in Zig. Through 2024–2025, Bun 1.x shipped, 1.1 and 1.2 followed, and Node compatibility now lands near 99%. In 2026, Bun is the single largest industrial proof that Zig can ship serious infrastructure software.

Why Bun chose Zig:

  • Compile speed — a JS runtime pulls massive C++ dependencies (JSC, V8), and Zig packages them cleanly inside its build system
  • zig cc for cross-compiling C/C++ deps — Bun builds macOS/Linux/Windows binaries from one host
  • Allocator control — alloc/free is hand-tuned in the JS runtime hot path
  • comptime — boilerplate for bun:ffi, bun:sqlite is generated at compile time

A simplified slice of Bun-style code:

// Bun-style HTTP server hot path (simplified)
pub fn handleRequest(
    arena: *std.heap.ArenaAllocator,
    socket: *Socket,
    req: *Request,
) !void {
    var response_buffer = std.ArrayList(u8).init(arena.allocator());
    try writeStatusLine(&response_buffer, 200);
    try writeHeaders(&response_buffer, req);
    try socket.writeAll(response_buffer.items);
}
  • Arena allocator — memory is bundled per request; when the response is done the arena releases everything at once. malloc/free calls are nearly zero
  • Explicit error propagationtry rethrows all I/O errors upward
  • No hidden alloc — even ArrayList requires an explicit init(allocator)

What Bun shows: a small team (under 10 people) can build Node-class infrastructure in Zig. That is dramatically less code than Rust would need, and dramatically safer than C.


5. TigerBeetle — distributed accounting database

TigerBeetle, led by Joran Dirk Greef, is a financial distributed database. Written 100% in Zig with a single clear purpose: process double-entry accounting transactions at OLTP speed. Its target is the ledger backend of fintech and payment systems.

Why TigerBeetle is interesting:

  • NASA Power of 10 rules plus the TIGER STYLE coding guide — 70-line function limit, no recursion, dynamic alloc only at startup, explicit bounds on every loop, assertions everywhere
  • Deterministic simulation testing — VOPR (Viewstamped Operation Replication), their own consensus, fault-injected and simulated billions of times
  • Single binary, zero dependency — one zig build and you have a single binary
  • Performance — targeting one million transactions per second per node

A snippet in TIGER STYLE:

fn transfer(ledger: *Ledger, from: AccountId, to: AccountId, amount: u64) !void {
    assert(from != to);
    assert(amount > 0);

    const from_account = try ledger.lookup(from);
    const to_account = try ledger.lookup(to);

    assert(from_account.balance >= amount); // explicit precondition

    from_account.balance -= amount;
    to_account.balance += amount;

    try ledger.commit();
}
  • assert sits in the code like comments — debug builds enforce invariants
  • All alloc happens at startup; the hot path has zero alloc
  • Functions are short and do one thing

TigerBeetle is the industrial proof that Zig is viable for mission-critical software. A fintech ledger is a domain where being off by one cent can sink a company, and they bet on Zig.


6. Ghostty — Mitchell Hashimoto terminal

Ghostty is a GPU-accelerated terminal emulator built by Mitchell Hashimoto (the Vagrant / Terraform guy). The 1.0 release shipped in December 2024, and by 2026 it is a first-class citizen on both macOS and Linux.

Why Ghostty chose Zig:

  • Cross-platform native UI — Swift on macOS, GTK4 on Linux. The core logic in between is Zig
  • Comptime-generated terminal sequence parser — VT100 / xterm escape sequences become compile-time lookup tables
  • Zero-copy rendering — direct GPU texture writes, so alloc must be hand-managed
  • Mitchell personal pick — that the Terraform-in-Go author landed on "next is Zig" was an event itself

A pattern Ghostty highlights:

const Cell = struct {
    char: u21,
    fg: Color,
    bg: Color,
    attrs: packed struct(u8) {
        bold: bool,
        italic: bool,
        underline: bool,
        _reserved: u5,
    },
};
  • packed struct(u8) — explicit bit layout. What you would do in C with macros and comments, the language understands directly
  • u21 — exactly 21 bits, the perfect width for a Unicode code point

Ghostty is also proof that GUI apps can be built in Zig. New systems languages typically stall on GUI, and Mitchell answered: "core in Zig, platform chrome native".

A deeper comparison lives in a separate post (2026-05-16 terminal shell tools).


7. Zig build system (build.zig) and Zon manifest

Zig does not use an external build tool. Make, CMake, Bazel, Cargo disappear — replaced by one file: build.zig. That means the build script is also Zig code.

A typical build.zig:

const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
        .name = "myapp",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    b.installArtifact(exe);

    const run_cmd = b.addRunArtifact(exe);
    const run_step = b.step("run", "Run the app");
    run_step.dependOn(&run_cmd.step);
}
  • b: *std.Build — the build-graph builder object
  • b.standardTargetOptions — automatically accepts options like -Dtarget=x86_64-linux-gnu
  • Build is code, not data — for-loops, if-branches, dynamic dependencies all possible

Zon manifest — build.zig.zon

Zig Object Notation, introduced in 0.11. JSON-like, but written in Zig syntax.

.{
    .name = "myapp",
    .version = "0.1.0",
    .minimum_zig_version = "0.14.0",
    .dependencies = .{
        .httpz = .{
            .url = "https://github.com/karlseguin/http.zig/archive/abc123.tar.gz",
            .hash = "1220abcdef...",
        },
    },
    .paths = .{ "src", "build.zig", "build.zig.zon" },
}
  • .hash is required — content-addressed; a dependency change halts the build
  • No central registry — no npm/crates.io equivalent. Every dependency is a direct URL reference
  • This is intentional — Andrew said "package managers are too large a security surface"

Tradeoffs:

  • Pro: dependency graphs cannot explode; nobody pulls a left-pad-style stunt
  • Con: discoverability suffers — finding "the Zig version of lodash" means searching the wild

8. Zig as C cross-compiler — why other projects use Zig

The strongest tool that ships with zig is zig cc / zig c++. Not a wrapper — a first-class C/C++ compiler that bundles LLVM.

# Build a macOS aarch64 binary from a Linux x86_64 host
zig cc -target aarch64-macos main.c -o main

# Cross-compile to Windows x86_64
zig cc -target x86_64-windows-gnu main.c -o main.exe

Why this matters:

  • glibc version pinning-target x86_64-linux-gnu.2.17 for RHEL 7 compatibility
  • libc bundled — Zig ships musl, glibc, mingw sources along with itself
  • One install — installing Zig gives you cross-compilers for every target

Industrial usage:

  • uv (Python package manager, by Astral) — uses zig cc to build wheels
  • TigerBeetle — one zig build produces every platform binary on a single host
  • Bun — uses it for dependency compilation as noted above
  • gcc / clang replacement — folks like Drew DeVault have replaced toolchains in their projects

The key message: you end up using Zig the tool even when you do not use Zig the language. Cross-compilation alone is reason enough.


9. Zig vs Rust — the differences

By 2026, "Rust vs Zig" is already tribal. Both claim to be the C/C++ successor, but they feel very different.

AxisRustZig
Memory safetyBorrow checker (compile-time enforced)Allocator-aware + runtime checks
GenericsTrait + monomorphizationcomptime
Compile speedInfamously slowFast and getting faster (native backend)
Asyncasync/await (stabilized)Removed, redesign in progress
Standard libraryHugeSmall (intentionally)
Build toolcargobuild.zig
1.0May 2015TBD (late 2026 ~ 2027)
GovernanceFoundation plus teamsAndrew Kelley plus ZSF
Learning curveVery steepSteep, but different shape

The "no borrow checker, is it safe?" answer for Zig:

  1. Allocator is explicitly passed — function signatures spell out who owns the memory
  2. Runtime safety checks — debug builds auto-panic on out-of-bounds, integer overflow, null deref
  3. defer / errdefer — not RAII, but deterministic cleanup
  4. GeneralPurposeAllocator leak detection — leaks are caught in debug builds

Andrew Kelley said: "Rust borrow checker gives up too much freedom for safety; Zig chooses explicitness and simplicity". Both are correct. The right answer depends on domain.


10. comptime — the killer feature of Zig

comptime is the core differentiator of Zig. Arbitrary Zig code runs at compile time, and the results can be used as types and constants.

10.1 Generics are just functions

fn List(comptime T: type) type {
    return struct {
        items: []T,
        len: usize,

        pub fn append(self: *@This(), item: T) void {
            // ...
        }
    };
}

const IntList = List(i32);
const StrList = List([]const u8);
  • T is a comptime parameter — the type itself is passed as a value
  • List is a function; calling it returns a type
  • Same work as C++ templates and Rust generics — but first-class inside the language

10.2 Compile-time code execution

const lookup_table = comptime blk: {
    var t: [256]u8 = undefined;
    for (&t, 0..) |*v, i| {
        v.* = @intCast(i * 2 % 256);
    }
    break :blk t;
};
  • comptime blk: { ... } — this block runs at compile time and the result is baked in
  • At runtime, lookup_table is just 256 bytes of data
  • What you would handle in C with macros or external scripts, Zig handles in the same language

10.3 Code that inspects itself

fn serialize(value: anytype, writer: anytype) !void {
    const T = @TypeOf(value);
    const info = @typeInfo(T);

    switch (info) {
        .Int => try writer.print("{}", .{value}),
        .Bool => try writer.print("{}", .{value}),
        .Struct => |s| {
            inline for (s.fields) |field| {
                try serialize(@field(value, field.name), writer);
            }
        },
        else => @compileError("unsupported type"),
    }
}
  • @typeInfo introspects a type
  • inline for unrolls at compile time
  • @compileError raises a compile-time failure

This is the work that Rust does through proc-macro plus serde plus syn — done at the language level in Zig. No external macros.


11. Allocator-aware programming

The Zig memory model fits in one sentence: "every function that uses memory must take an allocator as a parameter".

pub fn readFile(allocator: std.mem.Allocator, path: []const u8) ![]u8 {
    const file = try std.fs.cwd().openFile(path, .{});
    defer file.close();

    const stat = try file.stat();
    const buffer = try allocator.alloc(u8, stat.size);
    errdefer allocator.free(buffer);

    _ = try file.readAll(buffer);
    return buffer;
}
  • Allocator is passed explicitly — the caller knows what alloc is used
  • defer closes the file
  • errdefer frees the buffer only on error

Standard-library allocators:

AllocatorPurpose
std.heap.GeneralPurposeAllocatorDebug builds detect leak / double-free
std.heap.page_allocatorDirect OS pages
std.heap.c_allocatormalloc / free
std.heap.ArenaAllocatorFree everything in one shot
std.heap.FixedBufferAllocatorPre-allocated buffer
std.testing.allocatorTests, leak detection enforced

The arena pattern is the workhorse:

var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const alloc = arena.allocator();

// Every alloc in this scope is freed in one shot via arena.deinit()
const a = try alloc.alloc(u8, 100);
const b = try alloc.alloc(u8, 200);
// No individual free needed for a or b

Bun HTTP request handling and TigerBeetle transaction processing both run on the arena pattern. Fewer alloc calls means consistently faster average performance.


12. ZLS (Zig Language Server)

ZLS is the community LSP for Zig (not the official one). By 2026, it is first-class in VS Code, Neovim, Helix, and Zed.

What ZLS provides:

  • Goto definition / find references
  • Autocomplete (covers comptime results to a degree)
  • Inlay hints — inferred types inline
  • Diagnostics — invokes the Zig compiler to surface errors live
  • Rename refactoring

Watch out for:

  • ZLS must match the Zig version. ZLS for 0.14 and ZLS for 0.13 are distinct
  • comptime inference has limits — especially with anytype parameters
  • Even at 0.14, ZLS still hangs occasionally — not Rust-analyzer-level stable yet

Config (.zls.json):

{
  "enable_inlay_hints": true,
  "enable_argument_placeholders": false,
  "warn_style": true,
  "enable_autofix": true
}

13. Roc — the functional alternative

Roc is a functional systems language by Richard Feldman (from the Elm community). Strangely, it is often mentioned alongside Zig. Why:

  • The Roc compiler is written in Zig
  • roc build also bundles LLVM — same approach as Zig
  • Functional, no GC, ML-family syntax — Haskell aesthetics plus Zig execution model
  • Andrew Kelley publicly cheers it on

A taste of Roc:

greet : Str -> Str
greet = \name -> "Hello, \(name)!"

main =
    Stdout.line! (greet "World")
  • Functional, pattern matching, type inference
  • ! marks effects — same impulse as Haskell IO
  • No GC; reference counting plus opportunistic in-place mutation

In 2026, Roc is still pre-1.0. But there is real space for "a serious ML-family language built on Zig", and for people who lean functional it is attractive. A case study in the diversity of the Zig ecosystem.


14. Korea / Japan — adoption at builders, Cybozu Zig experiment

14.1 Korea — Builder (SaaS) adoption

Korean SaaS builder companies (Notion-style or Webflow-style tools) have been experimenting with Zig in their core rendering engines since 2025.

  • Reason 1: WASM target — Zig supports WASM as a first-class target, and the resulting binaries are often smaller than Rust
  • Reason 2: zig cc brings existing C/C++ dependencies into WASM — less reliance on emscripten
  • Reason 3: it fits small core teams in startups — onboarding is cheaper than Rust

Of course, it is still small. JS/TS dominates. But in "WASM-target hot paths", Zig adoption is rising.

14.2 Japan — Cybozu Zig experiment

Cybozu (groupware company behind Kintone) publicly shared an experiment migrating modules from Go to Rust to Zig. Findings:

  • Rust: safe but blocked by compile speed and learning curve
  • Zig: faster compiles, and engineers coming from Go could read it within a few days
  • Downside: it is pre-1.0, so every compiler upgrade means code edits — the question is whether that cost is acceptable

Some Fujitsu and NTT infrastructure teams also adopted zig cc alone to simplify build pipelines — the "not the language, but the tool" pattern.


15. Who should pick Zig — systems / embedded / C replacement

Take Zig seriously in these domains:

  1. Build-system replacement for a C/C++ project — anyone tired of Make/CMake. zig build plus zig cc cleans it up
  2. CLI tools that need cross-compilation — the Bun pattern
  3. Embedded / OS / firmware — anywhere you need no std, exact bit layout, allocator control
  4. WASM-target hot paths — the builder case
  5. Mission-critical financial backends — TigerBeetle, assuming you have TIGER-STYLE discipline

Where to avoid:

  • Plain web backends (CRUD) — Go, Node, Rust are much faster to ship
  • Data science / ML — Python ecosystem dominates
  • Full GUI apps — keep only the core in Zig, use native for chrome (Ghostty pattern)
  • Long-term stability critical — 1.0 is not out. Every version bump may demand migration

One-line conclusion: Zig in 2026 is "a serious language in a serious experimental stage". Bun, TigerBeetle, and Ghostty shipping before 1.0 is the proof. Do not wait for 1.0 — try it in a low-stakes corner, then decide.


16. References