TigerStyle! (Or How To Design Safer Systems in Less Time) by Joran Dirk Greef

TigerStyle! (Or How To Design Safer Systems in Less Time) by Joran Dirk Greef — TigerBeetle

TigerBeetle

AI-assisted summary of TigerStyle! (Or How To Design Safer Systems in Less Time) by Joran Dirk Greef — original source. Check the original for full context.

Source published:

Key ideas

  • TigerStyle compresses traditional 10-year distributed database engineering lifecycles down to 4–5 years by pairing deliberate, upstream design restraint with dense runtime assertions and deterministic simulation. (0:00)
  • Software development velocity should be modeled across total lifecycle cost of ownership; typing speed only yields up to a 2x improvement, whereas solving problems in the design phase can yield 10x to 1,000x downstream returns. (5:00)
  • TigerBeetle eliminates post-startup dynamic memory allocation entirely, statically preallocating all message buffers and storage pipelines to bound edge cases, manage backpressure, and prevent out-of-memory crashes. (32:15)
  • Deploying a deterministic in-memory simulator that injects network partitions and aggressive storage corruptions uncovered and resolved 30 complex distributed bugs within its first three weeks of operation. (45:00)

Detailed notes

## Background Published by the TigerBeetle channel on YouTube on April 26, 2023, this video presents the final talk from the Systems Distributed '23 conference. The presentation, delivered by Joran Dirk Greef, introduces the TigerStyle engineering methodology and invites viewers to join the community discussion on the project's Slack workspace.

## TL;DR Joran Dirk Greef details TigerStyle, an engineering philosophy developed while building TigerBeetle that compresses the timeline for constructing mission-critical distributed systems without cutting corners. The methodology relies on three core tenets: evaluating the full development lifecycle to eliminate code early during design, asserting hard bounds and invariants via extensive tripwire assertions, and leveraging deterministic simulation testing to accelerate verification. By utilizing static memory allocation, fail-fast crash semantics, and Zig's compile-time safety, teams can eliminate technical debt and avoid production corruption. Greef demonstrates these concepts using TigerBeetle's deterministic simulator, VAPOR, as well as an interactive retro-themed browser game that visualizes Viewstamped Replication under extreme network partitions and storage corruption.

## Atmosphere & style The presentation is energetic, passionate, and charismatic, striking an engaging balance between deep systems engineering rigor and playful pop-culture metaphors ranging from Tron and The Lord of the Rings to classic chiptune video games. Greef speaks with evangelical conviction and warmth, candidly celebrating his engineering team and early collaborators while recounting grueling debugging mysteries. The live demonstrations shift the mood from reflective architectural philosophy to high-adrenaline excitement as terminal logs and retro WASM simulations withstand relentless fault injection, concluding the conference on an inspiring, community-minded note.

## Bulletin - [00:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=0s) TigerStyle compresses traditional 10-year distributed database engineering lifecycles down to 4–5 years by pairing deliberate, upstream design restraint with dense runtime assertions and deterministic simulation. - [05:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=300s) Software development velocity should be modeled across total lifecycle cost of ownership; typing speed only yields up to a 2x improvement, whereas solving problems in the design phase can yield 10x to 1,000x downstream returns. - [32:15](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1935s) TigerBeetle eliminates post-startup dynamic memory allocation entirely, statically preallocating all message buffers and storage pipelines to bound edge cases, manage backpressure, and prevent out-of-memory crashes. - [45:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2700s) Deploying a deterministic in-memory simulator that injects network partitions and aggressive storage corruptions uncovered and resolved 30 complex distributed bugs within its first three weeks of operation.

## Key takeaways - [00:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=0s) The TigerStyle methodology rests on three core pillars: seeing the whole picture across the lifecycle, laying dense tripwire assertions, and leveraging deterministic simulation as a time machine. - [05:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=300s) Typing faster offers marginal speedups; the only order-of-magnitude engineering leverage lies in the design phase, where architectural breakthroughs can eliminate code entirely before it is written. - [10:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=600s) Software teams must resist rushing into implementation; TigerBeetle's deliberate 3.5-month initial incubation period yielded an estimated 1,000x return on execution velocity. - [12:38](https://www.youtube.com/watch?v=w3WYdYyjek4&t=758s) Testing complex distributed systems historically takes decades because verification is bounded by wall-clock time; breaking this bottleneck requires deterministic simulation. - [15:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=900s) Building mission-critical systems as 'painters' rather than 'sculptors' avoids bloat and performance regressions by composing from four fundamental colors—Network, Disk, Memory, and CPU—governed by latency and bandwidth. - [20:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1200s) Systems must explicitly define mechanical fault models upfront across all hardware layers and aggressively minimize surface area by limiting stateful components and network ports. - [22:31](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1351s) Enforcing a zero-technical-debt policy prevents compounding context-switching penalties, while biweekly walk-and-talk sessions help teams invalidate flawed ideas before implementation. - [25:07](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1507s) Functions must serve as self-verifying correctness boundaries that validate entry preconditions, parameter relationships, and postconditions, immediately shutting down upon invariant violations. - [28:13](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1693s) While typical programmers work within positive space, distributed systems environments act like hackers operating in negative space, requiring developers to defensively assert against unanticipated states. - [30:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1800s) Loops should never be unbounded; enforcing explicit upper and lower bounds prevents deadlocks, runaway compute usage, and unverified minimum work. - [37:28](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2248s) Operating errors that can be gracefully handled must be strictly separated from programming bugs, which represent undefined logic territory that must trigger an immediate fail-stop crash. - [40:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2400s) In distributed replicated systems, crashing quickly on invariant failure preserves safety and high availability, whereas limping along risks silent and catastrophic data corruption. - [40:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2400s) Zig provides an ideal foundation for memory-intensive systems programming through default-checked arithmetic and compile-time execution ('comptime') for verifying structural invariants. - [50:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=3000s) The VAPOR deterministic simulator validates strict serializability and consensus correctness under extreme fault models, including simultaneous 30% storage read and write corruptions. - [52:45](https://www.youtube.com/watch?v=w3WYdYyjek4&t=3165s) Compiling TigerBeetle's real production codebase into WebAssembly demonstrates how deterministic simulation can be run entirely in the browser to visualize consensus under network partitions and storage faults.

## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=0s) The Magic Trick: Velocity Through Style

Joran Dirk Greef introduces what he calls a "magic trick" for tackling the most complex software challenges conceivable, such as consensus protocols, distributed databases, operating systems, self-driving vehicle software, or mission-critical medical devices. Typically, these systems require a decade or more of painstaking engineering. Greef argues that applying this particular style allows an engineering team to accomplish that same build in four to five years without cutting corners: they work hard, but with vastly greater velocity. The resulting architecture is safer, faster, simpler to operate, and robust enough to allow engineers to sleep soundly at night.

```mermaid flowchart TD accTitle: The Three Core Pillars of TigerStyle accDescr: Diagram showing how TigerStyle combines seeing the whole picture, checking assertions, and time machine testing to compress system build time. subgraph TS["TigerStyle Methodology"] P1["1. Sees the Whole Picture<br>(Design & Lifecycle TCO)"] P2["2. Checks Yourself Before You Wreck Yourself<br>(Assertions & Preconditions)"] P3["3. Gives You a Time Machine<br>(Deterministic Simulation Testing)"] end P1 --> OUT["Safer, Rock-Solid Systems in 50% of the Time"] P2 --> OUT P3 --> OUT ```

This methodology emerged directly from the creation of TigerBeetle, an open-source, dedicated distributed financial accounting database engineered specifically for mission-critical safety and high throughput. Greef notes that he learned this development philosophy from pioneers such as Gerard Holzmann (author of the Spin model checker and NASA/JPL's coding rules) and James Cowling (known for consensus and distributed storage work). Because this philosophy feels unfamiliar and counterintuitive to standard industry practices, observers often label it "crazy." Greef's explicit goal for the talk is to shift how developers think about programming so that building systems once considered unreachable comes within grasp.

Greef connects this engineering style to recent breakthroughs in testing, referencing Will Wilson's presentation on deterministic simulation testing at Antithesis. To explain how TigerBeetle went from an idea to a running production database and fostered a community—giving credit to early supporters and collaborators including Hardcore, Zig SHOWTIME, Amplify, Coil, Adrian, and co-founder Don, who originally coined the name TigerBeetle—Greef breaks the methodology down into three mutually accelerating concepts: 1. Seeing the whole picture 2. Checking yourself before you wreck yourself 3. Giving yourself a time machine

As Greef puts it when describing the transformative impact of the methodology: “And the way that we developed Tiger Beetle is something that gets our team excited. Because when you see these systems run the way they do, it's hard to describe. It's like magic.”

- TigerBeetle was created less than three years prior to the talk as a mission-critical distributed ledger for financial accounting. - Don, present in the audience, is credited as the co-founder who created the name TigerBeetle alongside Greef. - The overarching promise of TigerStyle is that it "buys you time" by pulling future development velocity into the present.

---

[05:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=300s) Seeing the Whole Picture and the Total Cost of Ownership

Greef begins with the premise that the audience already possesses what he terms "the blessing of a hard problem"—a high-value, non-trivial engineering challenge that justifies serious effort. When developers start such projects, they frequently succumb to the appeal of popularity, adopting languages and ecosystems simply because they are trending or because they want a resume boost. While engineers often mock the logical fallacy of appealing to authority, they routinely select stacks like Rust, npm dependencies, or GraphQL merely because everyone else uses them. Greef clarifies that developers should choose tools on quality rather than quantity: Rust, for instance, should be chosen for its borrow checker and memory safety guarantees, not because of industry hype.

```mermaid flowchart LR accTitle: System Development Lifecycle Cost Equation accDescr: Horizontal progression through the phases of engineering showing how poor upstream design amplifies downstream cost. D["Design Phase"] --> C["Coding Phase"] C --> T["Testing Phase"] T --> P["Production Operations & Incidents"] ```

TigerStyle deliberately disrupts standard instincts by forcing developers to practice strict restraint. Developers are required to pause, deliberate, and delay coding. Even when coding finally starts, the process is deliberately slower and more methodical. Greef compares this mindset to a disciplined martial artist: by practicing restraint upstream, overall delivery accelerates. The fundamental error of modern software engineering is reducing system creation entirely down to the coding phase, ignoring design, testing, and day-to-day operational realities. TigerStyle instead views the software lifecycle through total cost of ownership, expressing development time and cost as an interrelated function of design, coding, testing, and production incidents:

$$\text{Time / Cost} = f(\text{Design}, \text{Coding}, \text{Testing}, \text{Production Incidents})$$

Trying to speed up software delivery by typing faster yields at most a 2x improvement, which Greef dismisses as marginal and immaterial when tackling distributed systems. True order-of-magnitude leverage exists exclusively in the design phase. A high-leverage design makes it possible to eliminate code entirely before it is ever written. Conversely, rushing out of design into coding creates catastrophic downstream costs: a flaw caught in production inflicts real customer damage and requires months of painful coordination, patch development, and deployment planning to remediate.

Highlighting how engineering restraint generates speed, Greef emphasizes: “And if we move too early out of the design phase and into coding testing production, a bad design will become more and more expensive and take longer and longer to fix.”

- Merely increasing typing speed or working more hours caps productivity gains at roughly 2x, which fails to move the needle on hard problems. - Thoughtful architectural design can completely design away entire categories of code, sparing the team from having to write, test, and maintain them. - Decisions made at the engineering level (such as choosing static memory allocation) directly impact business survival, operational costs, and product differentiation. - Bugs that escape into production inflict severe compounding costs, turning minor architectural oversights into multi-month mitigation efforts.

[10:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=600s) Deferring Code, the Ent Mentality, and the 1,000x Return on Design

Engineers often struggle with the tension between cold objectivity and the subjective notion of elegance. Invoking Edsger W. Dijkstra, Joran Dirk Greef explains that simple, elegant architectures do not just look neat on paper; they execute faster and fail far less often. He urges developers to be like the Ents from *The Lord of the Rings* when heading to war: deliberate, unhurried, and a force to be reckoned with. The central discipline is to deliberately postpone writing code until the high-level design rests on solid footing. If the back-of-the-envelope calculations do not add up or an engineer's intuition signals that something is amiss, they must pause rather than rushing ahead. Instead of settling for an early local maximum, engineers need to act like Frodo and Sam marching toward Mordor—locating the absolute steepest, fiercest challenge, the global maximum on the slopes of Mount Doom, and designing directly for that worst-case reality.

```mermaid flowchart TD accTitle: Iterative Design Cost vs Production Cost accDescr: Diagram illustrating how cheap conceptual iteration prevents expensive late-stage rewrites.

subgraph Ideation["Design Phase (Cheap Iteration)"] A["Sketch System Ideas"] --> B{"Numbers Match & Gut Check?"} B -- "Unresolved Issues" --> C["Discard & Re-sketch"] C --> A end

subgraph Implementation["Downstream Delivery"] B -- "Slopes of Global Maxima" --> D["Write Production Code"] D --> E["Rigorous Testing & Validation"] end ```

In TigerBeetle's case, this patient restraint was partly an accident of timing. Greef notes that the project had a three-and-a-half-month incubation period from March to July 2020 before any coding began, allowing critical concepts to simmer. Without that forced gestation, TigerBeetle's most fundamental architectural choices would have been missed entirely. Spending an extra month on design across a two-year timeline amounts to a negligible 4% of the total project schedule, but that minor delay reaps exponential rewards. Redoing sketches on paper or batting concepts around with teammates costs almost nothing, whereas discovering a structural flaw after implementation forces devastating rewrites. For TigerBeetle, Greef calculates that this initial 4% time investment delivered an astounding return on execution velocity.

- Edsger W. Dijkstra on the practical power of elegance: “The simple and elegant systems tend to be easier and faster to design and get right, more efficient in execution and much more reliable.” - Greef on resisting the impulse to start typing prematurely: “So I think the discipline is to defer coding, to wait, until you have the design roughly right.” - On the massive payoff of the upfront simmering period: “And over the course of, say, like a two-year project, that extra month is only 4%. When you compare it with the coding and testing phases, yet that 4% is going to give you like a 10x impact on those phases or 20x or 1000x for TigerBeetle was literally 1000x.”

---

[12:38](https://www.youtube.com/watch?v=w3WYdYyjek4&t=758s) The Reality of Testing Complex Systems and the Need for a Time Machine

While simple programs can be tested in a timeframe roughly equal to their development time, complex storage and distributed systems belong to an entirely different league where testing dominates the lifecycle. Historically, proving database correctness takes decades—an ongoing saga exemplified by PostgreSQL working through storage edge cases like "FsyncGate" three decades in. Industry gold standards like Kyle Kingsbury's Jepsen tool are indispensable for finding distributed anomalies, but they are bounded by wall-clock time: if a distributed race condition takes seven continuous days of load to manifest, verifying the bug and verifying its eventual fix each require a full week of runtime.

To escape this timeline trap, distributed systems engineers require a conceptual "time machine" that can manipulate time deterministically. If an engineering team spends two years building a high-performance consensus engine and storage layer like TigerBeetle, industry precedent dictates an evaluation window of at least five to ten years to weed out catastrophic edge cases. Accelerating that verification phase down to a single year represents a true 10x "silver bullet." Navigating this trade-off requires zooming out to appreciate how intentional slowness in early design enables unprecedented downstream speed.

- Joran Dirk Greef on what truly drains engineering resources in infrastructure: “Testing I think this is my opinion, but I think testing is what really dominates the development of complex systems most of all and the reason is that for simple systems people say you know well testing time is is roughly equivalent to the coding time... but for complex systems testing can take longer, sometimes decades.” - On the inherent operational cost of real-time fuzzing and testing frameworks: “Postgres took 30 years and we're still solving FsyncGate, you know, in 2020. So for example, like a tool like Jepsen is terrific at testing distributed databases. But even with Jepsen, if you want to find a bug that takes a week to manifest, you have to run Jepsen for a week.” - Greef on compressing the multi-year testing lifecycle: “If it took two years to build TigerBeetle, what if we could test it in a year? Instead of four, that would be a silver bullet. Because normally to test something like TigerBeetle is 10 years.”

---

[15:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=900s) Bio-Digital Jazz, Painters vs. Sculptors, and the Four Colors of Systems

Great software extends beyond textbook engineering; it combines empirical rigor with creative human artistry. Greef invokes his favorite fictional hacker, Kevin Flynn from *Tron*, who celebrated this synthesis as "bio-digital jazz"—the interplay of a rigid, tight logical beat with a soulful, organic groove. Deciding to build software as art forces teams to confront their fundamental creative mode, a dynamic captured by record producer Daniel Lanois's taxonomy of musicians: sculptors versus painters. Sculptors, such as Bob Marley carving an album out of a massive wall of sound, begin with a heavy block of material and chip away until only the essential reggae cadence remains. Painters, such as The Beatles composing in the studio with nothing more than blank paper or odd percussion items, build their canvas layer by layer from nothing.

Modern software engineering leans heavily toward sculpting: teams drag in enormous third-party frameworks and external dependencies, hacking them down to shape their product. For TigerBeetle, however, grafting outside dependencies would have ruined safety, ballooned testing times, and degraded throughput; creating something as singular as "Strawberry Fields Forever" required painting out of thin air. In the systems painting palette, there are only four fundamental colors—Network, Disk, Memory, and CPU—governed by just two shared textures: latency and bandwidth. By running back-of-the-envelope calculations across those four dimensions during design sketches, engineers can verify physical feasibility and avoid latency spikes long before committing code, and even take the time to solve the "second hardest problem" in computer science: naming things well.

- Joran Dirk Greef on the dual nature of programming: “If you believe that software is at least engineering and also more than engineering, that there's a precision and an art to software, that there's hard data, cold logic, and also what people bring to the table, human experience, creativity, information, inspiration.” - Greef on TigerBeetle's deliberate refusal to rely on off-the-shelf codebases: “However, we didn't build TigerBeetle like that because we zoomed out and we saw that the dependencies were going to slow down testing. They were going to introduce safety and performance risk. So we realized it was actually just going to be faster to paint TigerBeetle.” - Defining the low-level systems canvas: “And for systems, I think these are all the colors you need right, this is it, like just remember this: Network, disk, memory, CPU... because the two textures that all these paints share is latency and bandwidth.” - Greef's parting rule of thumb for the drafting phase: “You want to try and sketch as many different solutions as you can in the design phase because sketches are cheap, rewrites are expensive.”

[20:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1200s) Mechanical Fault Models and Reducing Surface Area

Good naming is not merely an aesthetic concern; having your nouns and verbs precisely right proves that you understand the system and possess a clear mental model. Humans have an inherent fault model—we make mistakes, and clear naming reduces miscommunication. Machines, likewise, operate under specific mechanical fault models that must be explicitly defined from the outset, spanning the network, storage, memory, and CPU. For a database like TigerBeetle, memory allocation failure cannot simply result in a panic—it must be safely handled. Other systems operating at massive hyperscale must reckon with ECC memory limitations, CPU bit-flips, and Byzantine fault tolerance. As Joran Dirk Greef emphasizes, “The big point is just be explicit upfront about what your system should tolerate.”

```mermaid flowchart TD accTitle: Fault Model and Surface Area Reduction accDescr: Mapping explicit mechanical fault models to architectural surface area reduction strategies.

subgraph Faults["Mechanical Fault Models"] direction TB F1["Storage Faults: Bit rot & disk slowness"] F2["Memory Faults: Allocation failures"] F3["CPU & Network: Scale & Byzantine faults"] end

subgraph Reductions["Architecture & Surface Area Reductions"] direction TB R1["Keep components stateless & off disk"] R2["Handle allocation safely without panic"] R3["Consolidate into single-port processes"] end

F1 --> R1 F2 --> R2 F3 --> R3 ```

Once those fault models are established, the architecture can be engineered to minimize exposure to faults in the first place. If the storage fault model anticipates bit rot or disk latency spikes, the design should deliberately keep as many components off the disk as possible, because stateful systems are notoriously hard to build. Beyond storage, total surface area must be ruthlessly trimmed. A system with two hundred components overwhelms human comprehension, whereas a system with three is manageable and vastly reduces unexpected interactions. Joran notes that this principle extends straight down to networking: rather than opening multiple listening ports across a single process for different roles, consolidating down to a single port dramatically simplifies deployment, reduces operational exposure, and boosts efficiency.

- Defining fault models explicitly covers network, storage, memory, and CPU failure modes. - In database engines like TigerBeetle, memory allocation failures must be safely caught and handled rather than triggering a crash or panic. - Minimizing stateful components limits bit rot and storage bottleneck exposure. - Limiting the raw count of components avoids cognitive overload and unpredictable emergent interactions. - Single-port process architectures minimize open networking attack surface and streamline operations.

---

[22:31](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1351s) Zero Technical Debt and Walk-and-Talk Design

Trimming surface area does not mean cutting corners. TigerBeetle maintains a strict zero-technical-debt policy. Joran jokes about who holds the line on the team—“I don't know which one of us is Gandalf. It's probably Adrian who wears the Gandalf hat.”—explaining that if an engineer discovers a better implementation while coding, they drop the current draft and do it the right way immediately. Deferring cleanup is a trap because the mental overhead of reloading context into your head later is massive, and no one can guarantee they will ever be granted the time to return and fix it. Doing it right the first time maintains forward momentum and keeps team morale high because everyone knows the foundational work is rock solid.

To break through stubborn design bottlenecks, the team relies on biweekly "walk and talk" sessions. Pacing triggers creative problem-solving in ways that sitting at a desk cannot. Joran paces the Cape Town boardroom for an hour on Bluetooth headphones with cameras off, while his colleague Fed walks the office halls and steps onto the balcony until his connection cuts out. They alternate pitching solutions, hunting for fatal counterexamples, and tearing down assumptions. The ultimate victory of a walk and talk is not merely solving a thorny problem, but realizing the entire problem can be engineered out of existence before a single line of code is written.

- TigerBeetle enforces a zero-technical-debt policy: if a superior approach is found during implementation, the team switches to it immediately. - Context switching and reloading complex system mental models later is treated as an unaffordable hidden cost. - High architectural standards create a positive second-order effect by motivating the engineering team and maintaining momentum. - Walk-and-talk sessions occur every two weeks with cameras off over Bluetooth headsets while engineers pace rooms or offices. - The primary objective in design walks is attempting to invalidate each other's ideas until the problem itself can potentially be designed away entirely.

---

[25:07](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1507s) Tripwire Assertions and Self-Verifying Functions

Shifting from system design into actual coding, Joran warns that adopting TigerStyle will initially slow engineers down, but it does so deliberately to accelerate all subsequent testing and review phases while guaranteeing high software quality. Regardless of how exhaustive code review is, subtle edge cases inevitably slip through—resembling the unexpected glitch that allows a player to jump straight through the ceiling in Super Mario. When testing and review reach their limits, the final line of defense is ensuring the software adheres to a strict binary guarantee: it must either operate cleanly within verified safety parameters, or immediately shut down the instant an invariant is violated.

Borrowing from Gerard Holzmann’s NASA Power of 10 rules for safety-critical code, TigerBeetle relies heavily on runtime assertions. Joran views assertions as defensive tripwires scattered deliberately throughout the codebase: “I love them because you're laying tripwires everywhere. You think, ah, someone is going to try and do this. I'm going to lay a tripwire there.” Functions should not exist solely for code abstraction; their primary duty is contributing to correctness. Every function checks its arguments upon entry, validates how those parameters relate to each other and global state, and asserts invariants over its return values upon exit. This makes the entire codebase continuously self-verifying under real workloads.

```mermaid flowchart LR accTitle: Self-Verifying Function Pattern accDescr: Logic flow of a function acting as a correctness boundary with entry assertions, execution, exit assertions, and fail-stop handling.

In["Input Arguments"] --> Pre["Assert Preconditions<br/>& State Relations"] Pre -->|"Invariant Violated"| Crash["Immediate Shutdown<br/>(Fail-Stop)"] Pre -->|"Valid"| Body["Execute Core Function Logic"] Body --> Post["Assert Postconditions<br/>& Return Values"] Post -->|"Invariant Violated"| Crash Post -->|"Valid"| Out["Return Verified Result"] ```

- Software must make a hard reliability guarantee: execute 100% safely within expected parameters or shut down instantly. - Assertion practices are inspired directly by Gerard Holzmann's NASA Power of 10 rules for safety-critical systems. - Function arguments, parameter relationships, and return values are rigorously validated via entry and exit assertions. - The fundamental purpose of procedural abstraction is reframed around enforcing program correctness. - Codebases saturated with tripwire assertions become self-verifying engines that catch subtle logic bugs early.

---

[28:13](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1693s) The Negative Space: Programmers vs. Hackers

Assertions enforce what an engineer expects to happen, but that highlights a critical division between ordinary programmers and hackers. Most programmers dwell exclusively in positive space: they want their code to work, operate with partially formed mental models, and assume that passing the compiler equates to correctness. Joran forcefully pushes back against this mindset: “I'll say it again, if it compiles, you've just begun.” In systems programming, having code compile is merely table stakes.

Hackers, by contrast, thrive entirely in the negative space. They do not enter through the front door, nor do they honor the compiler's assumptions; they actively attack edge cases and exploit scenarios the original author never conceived—much like the Mario runner finding a roof exploit that Nintendo engineers never imagined. When designing distributed systems, the operational environment itself acts as a relentless adversary. The network, disk bit rot, and unpredictable node crashes behave exactly like hackers, constantly probing the negative space of your assumptions. Surviving that environment requires adopting the adversarial mindset before deploying code.

- Standard programmers work in positive space, frequently assuming that compiling code implies logical correctness. - In TigerStyle and resilient systems programming, successful compilation marks only the starting line of engineering. - Hackers operate in negative space, searching for unhandled permutations and logic gaps that lie outside the author's expectations. - Distributed systems environments behave essentially like adversarial hackers, relentlessly exploiting unhandled edge cases, disk faults, and network partitions.

[30:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1800s) Thinking Like a Hacker and Bounding Loops

The production environment is inherently complex and adversarial, constantly finding novel ways to stress and break software. Developers must not merely plan for what they expect, but cultivate an adversarial mindset and ask what they do *not* expect. By determining what would be genuinely surprising or anomalous, an engineer can discover where the boundaries of the system truly lie and assert them. If an invariant is drawn too tightly, an assertion will trip, prompting the team to correct their mental model and discover the actual shape of the problem space.

A prime example is the ubiquitous unbounded `while (true)` loop used in data parsing. Greef asks the audience for a show of hands on who has stopped to consider the expected runtime of a `while (true)` loop they authored, noting that developers rarely interrogate whether a loop might run for an hour or spin infinitely, burning compute. Instead of unbounded loops, systems should rely on finite loops bounded by fixed minimum and maximum iterations. Setting an upper bound prevents runaway execution that can deadlock production systems, while establishing a lower bound ensures that the loop actually executes the required minimum work. As Joran Dirk Greef emphasizes: “So think like a hacker, you have to assert the lower bound.”

[32:15](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1935s) No Limits, 90s Dance Hits, and Static Memory Allocation

Greef laments that modern software architecture often mirrors the 1990s dance-pop anthem by 2 Unlimited: *No Limit*. When software is built without rigid boundaries, it ceases to be a comprehensible black box with defined edges and turns into an amorphous, "wobbly blob." Hardware resources—memory, disk, network, CPU, and concurrency—are inherently finite. Making these constraints explicit in code is essential to handling edge cases, designing effective backpressure, and managing load spikes, a principle central to Chapter 8 ("Handling Overload") of Google's *Site Reliability Engineering* book.

```mermaid flowchart TD accTitle: Error Taxonomy and Handling Strategy accDescr: Contrasting operational errors that require graceful handling with programming bugs that require assertion crashes.

subgraph OperationalErrors["Operational Errors (Expected Runtime Conditions)"] A["Resource Depletion / High Load"] --> B["Apply Backpressure"] B --> C["Reject New Connections / Shed Load"] C --> D["Keep Server Operating"] end

subgraph ProgrammingBugs["Programming Errors (Bugs / Invariant Violations)"] E["Violated Invariant / Loop Bound Exceeded"] --> F["Trigger Assertion Failure"] F --> G["Crash Process Immediately"] G --> H["Prevent Silent State Corruption"] end ```

Drawing directly from NASA's *Power of 10* rules for safety-critical systems, TigerBeetle enforces hard limits across its entire architecture: - TigerBeetle performs zero dynamic memory allocation after the startup phase. - All memory requirements are statically calculated up front, covering every message buffer permutation required by the consensus protocol. - The storage engine's memory allocations for compaction pipelines and concurrent disk/network tasks are fixed at initialization. - Every resource is strictly bound, making the system predictable and vastly simpler to reason about under stress.

As Joran Dirk Greef states: “A system has limits and you may as well make those limits explicit so that your design can handle all the edge cases.”

[35:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2100s) Test Velocity and the Sherlock Holmes Cache Bug

While upfront sizing makes the initial implementation phase slower, the payoff is a lean and highly performant codebase. Because static sizing forces developers to account for every byte, the resulting software avoids memory waste and can end up ten times more memory-efficient than dynamically allocated alternatives. Once those structural limits are defined in code, developers can assert against them continuously, turning assertions into an immense force multiplier for testing velocity. TigerBeetle runs thousands of assertions actively in production builds, providing immediate feedback whenever reality diverges from system invariants.

Greef shares a concrete example involving cache validation—one of the classic hard problems in computer science. In TigerBeetle, when a block of data is read from the in-memory cache, the system periodically reads the corresponding block from disk to assert that the cached data perfectly matches persistent storage without subtle cache-divergence issues. During benchmarking on an unusual workload, this cache assertion fired: - The team immediately knew a critical invariant had failed, despite having no explicit unit test coverage for that specific workload sequence. - Team member Jamie spent eight intensive hours tracking down the root cause, documenting the investigation in a blog post that Greef describes as reading like a Sherlock Holmes mystery novel. - Without that production-active assertion, the flaw likely would have slipped into production undetected for months or years before causing catastrophic data loss or corruption.

Reflecting on the power of this pattern, Joran Dirk Greef observes: “And that's because without this assertion, the bug might have taken months or years just to catch with catastrophic consequences.”

[37:28](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2248s) Language Defaults and the Flaw in Go's Assertion Philosophy

Despite their proven utility in catching catastrophic bugs early, assertions are widely neglected in mainstream codebases. Greef attributes this directly to programming language ergonomics and design philosophies, invoking computer scientist Barbara Liskov's observation that communicating new ways of thinking to programmers requires giving them new languages to think in. Too many existing languages either omit assertions entirely or disable them by default in production or optimized release builds—precisely where bugs possess the greatest potential to inflict catastrophic damage.

Greef highlights the Go programming language to illustrate this design failure. While TigerBeetle utilizes Go for operational tooling and respects many of its qualities, the official Go FAQ explicitly defends the absence of language-level assertions by asserting that programmers use them as an improper crutch to bypass disciplined error handling. Greef dismantles this perspective on multiple fronts: - Assertions are not syntactic conveniences for error handling; they are mission-critical verifications of programmatic correctness. - The fact that some developers misuse assertions does not justify stripping the tool from the entire ecosystem. - Most fundamentally, the Go stance conflates operational errors with programming bugs.

Operational errors are runtime conditions that software must handle gracefully—for example, if a server experiences memory pressure or fails an allocation, it should stop accepting new network connections and shed load rather than panicking globally. Conversely, programming errors are bugs that violate fundamental invariants, rendering the program's internal state untrustworthy. As Joran Dirk Greef puts it: “And most of all, the answer it conflates operating errors, which are errors that you can handle, with programming errors or bugs, which are errors you can't.”

[40:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2400s) Assertions, Crash-Only Safety, and Checked Arithmetic in Zig

Joran Dirk Greef tackles a classic developer objection regarding assertions and system failures: if a program can detect a programming error, why not catch it and handle it gracefully? His answer is that the premise is fundamentally flawed. Joran explains that “by definition, bugs can’t be predicted.” If a bug could be anticipated and handled cleanly, it should simply be fixed in the code from the outset. Once an invariant fails, the application has entered “undefined logic territory,” and attempting to limp along risks catastrophic data corruption.

```mermaid flowchart TD accTitle: Assertion Failure and Crash Recovery accDescr: Flow diagram showing how assertion failures trigger safe crashes and rapid recovery in distributed databases.

subgraph BugEncounter["Bug Encountered"] A["Programming Error Detected"] --> B{"Try to Handle Gracefully?"} B -->|"No: Undefined Territory"| C["Assert Fails / Immediate Crash"] B -.->|"Anti-pattern"| Bad["Data Corruption & Latent Bugs"] end

subgraph ClusterRecovery["Distributed Recovery"] C --> D["Fault-Tolerant Cluster"] D --> E["Restart Clean Process"] E --> F["Higher Availability & Visibility"] end ```

Addressing skeptics who claim mission-critical databases cannot afford to crash, Joran argues that correctness must trump raw uptime. In high-throughput financial systems processing tens of thousands of transactions per second, failing fast is the only safe choice. Joran emphasizes: “The question is not if you shut down, but look, how quickly can we? Because, you know, if you're going to shut down manually or automatically, that could be the difference between your business staying in business or going out of business.” In distributed systems running multiple replicas, a crash does not sacrifice high availability; rather, restarting an errant process purges the bad state and exposes latent bugs so they can be fixed.

Turning to language choice, Joran praises Zig as the ideal systems language for building database engines because databases are fundamentally exercises in memory manipulation. Unlike languages that disable safety checks or omit assertions by default, Zig actively enforces safety primitives. Andrew Kelley and the Zig compiler lay tripwires throughout the generated code, turning integer overflows into immediate assertion failures with clear stack traces. Joran argues that checked arithmetic enabled by default in safe builds should be the mandatory standard across all programming languages, calling any alternative “madness.” Furthermore, Zig allows developers to run arbitrary logic at compile time (`comptime`), verifying structural integrity and invariants before tests or production environments ever run.

- Assertions catch bad code and logic bugs, not runtime operational errors like network disconnects. - In distributed databases, fault tolerance allows processes to crash safely and reboot without compromising availability. - Zig enforces checked arithmetic by default in safe build modes; developers can selectively opt out with explicit block annotations where 100% test coverage exists. - Zig's compile-time evaluation (`comptime`) executes real code—not macro expansions—allowing design invariants to be verified during compilation.

---

[45:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2700s) Assertions as Living Documentation and the TigerBeetle Simulator

Beyond preventing bugs, assertions serve as a powerful teaching mechanism and architectural blueprint. As engineering teams scale, assertions define both the positive space (the intended logic) and the negative space (states that should never exist). Joran reflects on his own career, sharing that he has been coding for 28 years but only truly grasped the power of assertions over the past seven. To bridge that gap, TigerStyle leverages a deterministic simulation testing framework—a virtual "time machine" capable of advancing time at high speeds.

This simulator models the physical environment of a TigerBeetle cluster entirely in memory. It subjects nodes to extreme, adversarial conditions: injecting simulated network latencies, partitioning nodes, and corrupting storage across all disk replicas simultaneously. Throughout these simulated fault injections, the framework continuously verifies strict serializability and asserts that no data corruption occurs. Joran highlights the sheer productivity gains unlocked by deterministic testing: “When we first switched on our simulator, we found and fixed 30 bugs, like crazy distributed bugs, in just three weeks. Every day was just adrenaline.”

Joran shifts into a live walkthrough of `simulator.zig` from the TigerBeetle repository. He points to how Zig constructs the distributed database cluster at compile time by assembling pluggable, modular components into an in-memory replica. While joking that viewing text-based terminal logs might not be as visually flashy as a Super Mario demo, he stresses that deterministic execution seeded by a single pseudorandom number allows developers to reproduce and diagnose intricate, distributed race conditions within seconds.

```mermaid flowchart LR accTitle: TigerBeetle In-Memory Simulator Architecture accDescr: Diagram illustrating how the TigerBeetle simulator passes pluggable components to build an in-memory replica cluster driven by a random seed.

subgraph Config["Deterministic Seed & Config"] Seed["Random Seed"] Time["Virtual Time Source"] end

subgraph Assembly["Compile-Time Assembly (comptime)"] SM["State Machine Logic"] MB["Simulated Message Bus"] Storage["Faulty Disk Backend"] end

subgraph Cluster["In-Process Simulation"] Replica["In-Memory Replicas"] Check{"Assert Invariants & Strict Serializability"} end

Config --> Assembly Assembly --> Replica Replica --> Check ```

- High-density assertions capture the negative space of a program, explicitly documenting invariant violations directly inside the codebase. - The TigerBeetle simulator models network faults, storage delays, and concurrent disk corruption across all replicas up to theoretical limits. - The simulator discovered 30 complex distributed bugs in its first three weeks of operation, saving an estimated hundreds of engineering person-years. - In `simulator.zig`, components like the state machine, message bus, storage backend, and time source are supplied as pluggable compile-time parameters to construct the replica cluster.

[50:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=3000s) Deterministic Simulation Testing: The VAPOR Simulator

Joran Dirk Greef describes how TigerBeetle designs and controls its network simulation environment. Instead of relying on real-world randomness, the simulator explicitly configures latency distributions, packet drops, network partitions, and asymmetric latencies and bandwidth between nodes where one replica talks slower to another than in reverse. However, TigerBeetle goes far beyond standard network faults: “This is very, I don't know of other systems that really do storage fault corruption on the read and write paths.” While other systems rarely inject storage corruption during active read and write operations, TigerBeetle actively corrupts disk operations at high rates: "With TigerBeetle we just like to corrupt like 30% of the time, like everything... If you're going to read something, 30% of them corrupt it. Write something? Let's just write it to the wrong place. It's just more fun, right?"

```mermaid flowchart TD accTitle: TigerBeetle VAPOR Simulation Pipeline accDescr: Deterministic fault injection simulator testing client requests and replica state transitions against strict serializability.

subgraph Inputs ["Deterministic Seed"] S["Seed & Config"] C["27 Clients"] end

subgraph Faults ["Injected Faults"] NF["Network Faults<br/>(34% message replays, asymmetric latency, drops)"] SF["Storage Faults<br/>(3% read corruptions, 3% write corruptions)"] end

subgraph Cluster ["3 Replicas (VSR)"] R0["Replica 0"] R1["Replica 1"] R2["Replica 2 (Leader)"] end

subgraph Oracle ["Global Checker"] O["Global Omniscient Oracle<br/>(Strict Serializability, Liveness, Consensus Checks)"] end

S --> NF S --> SF C -->|"Requests"| Cluster NF -.-> Cluster SF -.-> Cluster Cluster -->|"State Transitions"| O ```

To demonstrate this in action, Greef boots up TigerBeetle's simulation tool, named the VAPOR (Viewstamped Operation Replicator), paying homage to the WOPR war simulator from the movie *WarGames*. Running from a deterministic random seed, the simulator spins up a logical TigerBeetle database of three replicas subjected to requests from 27 simulated clients. For this specific run, the simulator replays network messages 34% of the time to heavily test idempotency, while corrupting 3% of reads and 3% of writes. As it executes, the terminal visualizes Replica 1 transitioning across states, which the simulator immediately verifies against strict serializability. Because the simulator possesses omniscient global visibility over all in-flight client requests, it knows exactly what the valid state of the entire universe must be. When Replica 0 experiences issues, the cluster elects Replica 2 as the new primary, advances state, and completes the simulation successfully without consensus violations, deadlocks, or liveness failures.

- **VAPOR Name Origin:** Named after the Viewstamped Operation Replicator, referencing the WOPR supercomputer from the film *WarGames*. - **Cluster Parameters:** 1 deterministic seed driving 3 replica nodes and 27 client workloads. - **Fault Probabilities in Run:** 34% network message replay rate, 3% storage read corruptions, and 3% storage write corruptions. - **Verification Capabilities:** Real-time checking of strict serializability, consensus violations, and liveness/deadlock detection.

---

[52:45](https://www.youtube.com/watch?v=w3WYdYyjek4&t=3165s) Leveling Up: Compiling TigerBeetle to WASM for Browser Games

Greef transitions from the terminal-based simulator to a skunkworks project created with Joey and Fabio, whom Greef met in Milan in April of the previous year. Taking advantage of the development velocity unlocked by TigerStyle, the team decided to turn deterministic simulation testing into an interactive retro video game. Because TigerBeetle is written in Zig, the entire distributed database engine compiles directly to WebAssembly (WASM), allowing real TigerBeetle production code to execute inside a web browser.

Inspired by retro gaming cartridges, the team created three levels: **City Breeze**, **Red Desert**, and **Radioactive**. Greef loads the first level, City Breeze, which represents the utopian condition assumed by typical distributed systems code where nothing ever fails: no crashes, zero network faults, and perfect conditions. As upbeat chiptune synth music blares through the speakers, Greef emphasizes the role of sound in engineering: “You need to have a playlist called Sega Tracks or something, it really like motivates the team.” The animated beetles descend to the field and rapidly complete their Viewstamped Replication rounds under pristine conditions. The test passes effortlessly, prompting the audience to cheer and demand they level up.

---

[55:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=3300s) Red Desert: Network Partitions and Mexican Standoffs

The presentation transitions to the second level, **Red Desert**, which introduces an ominous change in soundtrack and a severe jump in difficulty. This level models hostile network dynamics akin to Jepsen testing: dropped packets, partitioned nodes, and asymmetric delays. Two beetles vie for leadership in what Greef calls a "Mexican standoff," triggering leader elections and deterministic view changes when the current primary crashes. The simulator UI allows the player to select units in real time, like an RTS game such as *StarCraft*, while watching individual nodes transition between preparing, backing up, and acknowledging operations.

Greef points out one of the beetles on screen, comparing it to Captain America as it assumes leadership, before watching the primary crash and another beetle recognize itself as the next rightful leader. Greef highlights a structural advantage of their consensus protocol: “VSR is a lot cooler I think because you can predict the next leader so we exploit that entirely.” Unlike consensus protocols that select leaders randomly or endure split votes, Viewstamped Replication features deterministic primary succession. Despite severe network partitions—faults that Kyle Kingsbury (Aphyr) would love—TigerBeetle completes all state transitions without crashing or violating consistency.

---

[57:38](https://www.youtube.com/watch?v=w3WYdYyjek4&t=3458s) Level 3: Radioactive Disk Corruption and Cosmic Rays

Rather than stopping at network testing, Greef launches the third and most grueling level: **Radioactive**. The level introduces simulated radioactive green slime and cosmic ray zaps that directly corrupt storage writes and reads while operations are in flight. As the music intensifies, bright visual lightning strikes represent cosmic rays hitting disks across the replica cluster.

The cluster sustains intense read/write disk corruptions across 49 consecutive operations while executing in-browser via WASM. Despite repeated storage errors, TigerBeetle detects the corrupted blocks, fetches repaired data from peers, and completes the entire test run without data loss or panics. Greef credits Joey, Fabio (who built the browser game engine), and Bia from Brazil (who created the pixel art animations) for building the visualizer.

---

[01:00:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=3600s) Closing Systems Distributed '23: Paying It Forward

Greef concludes the talk and brings the Systems Distributed '23 conference to a close, expressing gratitude to the attendees, the Cape Town venue, and the international speaker lineup. He shares an anecdote about inviting Will: “I emailed him to invite him and he replied he said, ‘Yeah, I've booked my flight,’ and I had barely given him the dates or anything.”

Greef thanks speakers who traveled from across the globe to attend: - Will and other speakers traveling from the United States and Europe - Tiamat and Terapai traveling from India - Imre traveling from Hungary - Lindsay traveling from Salt Lake City

Greef extends special praise to his TigerBeetle co-founder Phil (Ful), who organized the logistics and operations to make the conference run smoothly, as well as the wider TigerBeetle team. In closing, Greef reflects on the community ethos behind both TigerBeetle and the conference, noting that speakers traveled entirely at their own expense to share their knowledge with the local ecosystem: “So I guess that's the final lesson of TigerStyle: pay it forward and have fun as you build systems distributed.”

## Notable quotes > [15:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=900s) You want to try and sketch as many different solutions as you can in the design phase because sketches are cheap, rewrites are expensive. — Joran Dirk Greef

> [32:15](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1935s) A system has limits and you may as well make those limits explicit so that your design can handle all the edge cases. — Joran Dirk Greef

> [28:13](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1693s) If it compiles, you've just begun. — Joran Dirk Greef

> [10:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=600s) I think the discipline is to defer coding, to wait, until you have the design roughly right. — Joran Dirk Greef

> [20:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1200s) The big point is just be explicit upfront about what your system should tolerate. — Joran Dirk Greef

## Mentioned Joran Dirk Greef, TigerBeetle, Gerard Holzmann, Spin, NASA JPL, James Cowling, Will Wilson, Antithesis, Hardcore, Zig SHOWTIME, Amplify, Coil, Adrian, Don, Rust, GraphQL, Edsger W. Dijkstra, The Lord of the Rings, PostgreSQL, Kyle Kingsbury, Jepsen, Tron, Kevin Flynn, Daniel Lanois, Bob Marley, The Beatles, Fed, Super Mario, Google, 2 Unlimited, Jamie, Barbara Liskov, Go, Zig, Andrew Kelley, Joey, Fabio, WebAssembly, StarCraft, Bia, Phil, Tiamat, Terapai, Imre, Lindsay, Systems Distributed '23

## Video description Our final talk from Systems Distributed '23: https://systemsdistributed.com.

Join the chat at slack.tigerbeetle.com/invite!

[Source](https://www.youtube.com/watch?v=w3WYdYyjek4)

中文简报

TigerStyle!(或如何在更短时间内设计更安全的系统)——Joran Dirk Greef Joran Dirk Greef 详细介绍了 TigerStyle,这是在构建 TigerBeetle 过程中形成的一套工程哲学,旨在不走捷径的前提下缩短构建关键任务分布式系统的时间周期。该方法论依托于三大核心原则:评估整个开发生命周期以在设计阶段尽早精简代码、通过密集的触发断言来确立硬性边界与不变量,以及利用确定性模拟测试加速验证。通过采用静态内存分配、快速失败崩溃语义以及 Zig 的编译期安全特性,团队能够消除技术债务并避免生产环境中的数据损坏。Greef 借助 TigerBeetle 的确定性模拟器 VAPOR,以及一款在极端网络分区和存储损坏下可视化展示 Viewstamped Replication 协议的复古风互动浏览器游戏,演示了这些概念。 TigerStyle 通过将审慎的上游设计克制与密集的运行时断言及确定性模拟相结合,将传统的 10 年分布式数据库工程周期压缩至 4 到 5 年。 (0:00) 软件开发速度应综合整个生命周期的拥有成本进行建模;打字速度最多只能带来 2 倍的提升,而在设计阶段解决问题则可在下游产生 10 到 1000 倍的收益。 (5:00) TigerBeetle 完全摒弃了启动后的动态内存分配,静态预先分配所有消息缓冲区和存储流水线,以限制边缘用例、管理背压并防止内存不足导致的崩溃。 (32:15) 部署能够注入网络分区和激进存储损坏的确定性内存模拟器,在其运行的前三周内就发现并解决了 30 个复杂的分布式缺陷。 (45:00)

Reading summary

TigerBeetle co-founder Joran Dirk Greef argues that engineering teams can build complex, safety-critical distributed systems in roughly half the conventional time by enforcing strict upfront architectural restraint, saturating codebases with runtime assertions, and validating correctness using deterministic simulation testing. Upfront Restraint and Total Cost of Ownership (5:00) Greef contends that the traditional approach to software engineering conflates velocity with typing speed, which caps productivity gains at roughly 2x. In contrast, evaluating system development through total cost of ownership—spanning design, coding, testing, and production incidents—demonstrates that true order-of-magnitude leverage exists exclusively in the design phase. A well-conceived architecture can completely eliminate categories of code before they are written, avoiding compounding downstream costs where fixing an escaped production bug requires months of coordination. Mechanical Fault Models and Explicit Boundaries (20:00) Rather than relying on third-party frameworks, TigerStyle advocates painting systems from first principles across four hardware components—Network, Disk, Memory, and CPU—governed by latency and bandwidth. Systems must explicitly define mechanical fault models to handle issues like memory allocation failures, bit rot, and network partitions. To make components predictable under stress, TigerBeetle follows Gerard Holzmann’s NASA Power of 10 rules by enforcing hard boundaries, such as eliminating dynamic memory allocation after startup and strictly bounding all loops. Self-Verifying Functions and Crash-Only Correctness (25:07) While standard programmers assume that compiling code implies correctness, Greef emphasizes that the operating environment actively probes logic gaps like an adversarial hacker. To defend against this, functions should validate parameter relations at entry and return invariants upon exit using dense tripwire assertions. When an invariant fails, software must fail-stop immediately rather than attempting graceful recovery in an undefined state. In distributed systems, this crash-only strategy prevents silent data corruption while cluster replicas maintain availability. Deterministic Simulation Testing as a Time Machine (45:00) Testing complex storage engines historically requires years or decades due to wall-clock time constraints in distributed fault reproduction. To compress this verification cycle, TigerBeetle relies on an in-memory deterministic simulation harness driven by pseudorandom seeds, named the VAPOR simulator. By modeling network latency distributions, asymmetric delays, and active read/write storage corruptions alongside an omniscient validation oracle, the simulator surfaced and resolved 30 complex distributed bugs in its first three weeks of execution. Greef concludes that adopting strict design restraint, crash-fast assertions, and deterministic simulation transforms complex systems engineering from an unpredictable multi-year ordeal into a tractable, verifiable process, though it requires teams to deliberately unlearn mainstream programming habits and accept upfront friction.

精炼阅读摘要

TigerBeetle 联合创始人 Joran Dirk Greef 提出了名为“TigerStyle”的系统工程方法论,主张通过前期审慎的架构设计克制、贯穿运行时的防御性断言与确定性模拟测试,在不妥协安全性的前提下将分布式数据库等极复杂系统的交付与验证周期缩短一半以上。 架构设计的高杠杆与全生命周期成本 (5:00) Greef 认为,现代软件工程常将开发效率片面等同于编写代码的速度,忽视了设计、编码、测试和生产突发事件构成的全生命周期总体拥有成本(TCO)。提高打字速度或增加工时最多带来约 2 倍的局部提升,对分布式系统这一级别的工程挑战微不足道;真正能够产生数量级杠杆的是前期的设计阶段。优雅且简单的架构不仅在概念上更易理解,更能直接在物理层面上“设计掉”整类代码,免去后续编写、测试与维护的开销。相反,过早从设计进入编码会导致缺陷随时间推移变得极其昂贵,微小的架构疏忽往往在生产环境中演变成长达数月的补救工程。 为了获取设计的超额回报,TigerStyle 提倡类似《指环王》中“树人”(Ents)的克制心态,在架构未彻底验证前坚决推迟编码。TigerBeetle 在 2020 年正式动工前经历了三个半月的纯设计酝酿期,该阶段仅占两年项目周期的 4%,却为后续的实现与测试带来了数倍乃至上千倍的效率收益。Greef 借用音乐制作人 Daniel Lanois 的比喻,将软件构建区分为依赖外部依赖剪裁的“雕塑模式”与从零构建底层机制的“绘画模式”。为了消除第三方库带来的测试迟滞与安全隐患,TigerBeetle 坚持在系统底层围绕网络、磁盘、内存与 CPU 这“四种颜色”及其延迟与带宽特性展开原语绘制,并推行严格的零技术债务与定期的双人走谈(walk and talk)设计机制,确保在写下代码前即消除潜在缺陷。 显式故障模型与静态资源的硬性约束 (20:00) 构建高安全系统的基础在于对物理故障模型与资源边界进行前置定义。Greef 强调,开发团队必须预先明确系统在网络分区、存储位衰减(bit rot)、内存分配失败以及 CPU 异常等维度下应承受的极限。基于此,系统设计应尽可能减少有状态组件和暴露端口,将表面积降至最低以减轻认知负荷。在资源管理上,TigerStyle 批判了现代软件毫无节制消耗资源的倾向,全面践行 NASA 喷气推进实验室(JPL)的《Power of 10》机载安全代码准则:严格禁止在系统启动阶段之后进行任何动态内存分配。 在 TigerBeetle 中,所有共识消息缓冲区、状态机管道与磁盘任务所需内存均在初始化时完成静态分配,且所有循环结构都必须设定显式的最小与最大迭代边界,杜绝无界 while (true) 带来的死锁或算力失控风险。这种显式约束不仅迫使工程团队对每个字节的去向进行审视,使得内存使用效率相比动态分配架构提升近十倍,更在概念上清晰界定了两类异常的边界。Greef 借此批判了 Go 语言官方 FAQ 对断言的排斥态度,指出 Go 混淆了可以通过背压和限流优雅处理的“运行期操作错误”(Operational Errors),与代表系统状态违背了不可变式、属于代码缺陷的“编程错误”(Programming Bugs)。 负向空间思维、绊线断言与快速崩溃 (25:07) 在具体编码层面,Greef 区分了普通程序员与黑客的思维范式:程序员往往停留在正向空间,误以为代码能够编译通过即代表逻辑正确;而黑客与真实的生产环境则潜伏在“负向空间”,持续对作者未曾预料的边界情况与异常状态展开攻击。为了防范意外漏洞,TigerStyle 要求在函数边界密布由运行时断言构成的“绊线”。每个函数在入口处均会严格校验输入参数与其关联的全局状态,并在退出前验证返回值的不变量,使整个代码库成为一个在真实负载下持续自检的验证引擎。 针对业界“关键数据库绝不能轻易崩溃”的质疑,Greef 坚持主张“崩溃即安全”(crash-only safety)与快速失败原则。他指出,由于代码缺陷在发生时具有不可预测性,一旦核心不变量被打破,系统便已坠入未定义逻辑的深渊,强行维持运行只会引发静默的数据损坏。在具备冗余副本的分布式集群中,让异常节点立即崩溃重启并丢弃坏状态,远比容忍数据污染更具可用性与安全性。例如,TigerBeetle 在生产代码中部署了周期性校验内存缓存与磁盘底层数据一致性的断言,曾借此成功在数小时内排查出一起原本可能隐藏数年之久的复杂缓存分歧漏洞。此外,TigerBeetle 选用 Zig 语言实现,正是利用了其在安全构建模式下默认开启的算术溢出检查,以及可在编译期(comptime)执行任意代码验证设计不变量的特性。 确定性模拟测试与时间机器验证 (45:00) 复杂存储与分布式系统的验证周期通常主导了软件的整体交付时间,例如 PostgreSQL 在诞生三十年后仍在解决存储边缘问题,而常规基于物理时钟的混沌测试工具(如 Jepsen)复现一周的长周期并发竞态便需耗费整整一周的物理时间。为了突破这一时间瓶颈,TigerStyle 引入了确定性模拟测试(DST)作为“时间机器”。该测试框架将整个 TigerBeetle 集群及其物理运行环境完全置于单进程内存中建模,利用单一伪随机数种子作为唯一驱动源,使原本耗时数天的复杂分布式竞态能够被压缩至数秒内高保真重现。 在名为 VAPOR 的模拟器演示中,测试环境以受控概率人为注入非对称网络延迟、高达 34% 的消息重放,以及高达 3% 的读写双向磁盘数据损坏,同时依托全局全知检查器实时断言系统的严格可串行化与活性。Greef 透露,模拟器上线的前三周内便高密度捕获并修复了 30 个极其隐蔽的分布式并发缺陷,节省了数以百计的人年测试成本。得益于 Zig 能够直接编译为 WebAssembly(WASM)的技术特性,团队甚至将该仿真内核移植至网页浏览器中,制作成包含“微风都市”(理想环境)、“红色沙漠”(网络分区与选主对峙)及“高辐射区”(持续宇宙射线与磁盘损坏)三个关卡的复古像素游戏,直观展示了 Viewstamped Replication 共识协议在极端故障注入下依然能保持零数据丢失与状态推进的能力。 Greef 总结认为,TigerStyle 的本质在于将对代码严谨性、确定性验证工具的追求与工程趣味性相结合,并在行业社区中践行知识共享。然而,该方法论主要基于 TigerBeetle 这一底层高性能金融数据库的单点实践,其推崇的完全静态内存分配、拒绝外部第三方库以及自建全套模拟器的“全画卷”开发模式,对于拥有高频业务迭代需求或工程资源有限的通用应用软件而言,可能存在前期工程门槛过高、技术债务容忍度难以维系的适应性挑战。

Detailed notes — zh

## Background 本内容由 TigerBeetle 官方频道于 2023 年 4 月 26 日在 YouTube 发布,记录了 Joran Dirk Greef 在 Systems Distributed '23 大会上的闭幕主题演讲。视频简介指出这是该分布式系统大会的收官之作,并邀请观众加入其官方 Slack 社区交流探讨。 ## TL;DR Joran Dirk Greef 系统性地阐述了打造高安全、极限性能分布式金融数据库的核心工程哲学——TigerStyle。该方法主张通过“前期克制编码、注重顶层设计”来换取长期的交付速度,坚持零技术债务,并通过显式限制循环与全量系统资源(如启动后完全静态分配内存)彻底收敛暴露面。演讲深度剖析了断言(assertion)作为自验证“绊线”的防守价值,严厉批判了混淆操作错误与代码 Bug 的语言设计,并指出生产环境遭遇非预期缺陷时安全关机才是最优解。最后,团队展示了基于 Zig 语言构建的确定性单进程模拟器与运行在浏览器 WASM 中的游戏化故障注入演示,直观验证了 Viewstamped Replication 协议在网络分裂与严重磁盘损坏下的极致韧性。 ## Atmosphere & style 整场演讲洋溢着极客浪漫主义与对底层硬核工程的纯粹敬畏,节奏明快且极具感染力。Joran 宛如一位传道者,从引用《指环王》树人比喻到现场播放激昂电子乐进行浏览器 WASM 压力测试,在严谨的技术哲思与幽默风趣的即兴互动之间游刃有余。现场氛围热烈而充满张力,结尾对全球参会同行真挚动情的致谢,将硬核的系统编程升华为了关于传承、热爱与工程乐趣的动人篇章。 ## Bulletin - [10:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=600s) TigerBeetle 团队提出 TigerStyle 工程哲学,主张“慢即是快”并实行“推迟编码”:在动手写代码前用数月时间深思设计,仅需占工程周期 4% 的前期投入即可换取后续高达 1000 倍的效率与安全性回报。 - [30:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1800s) 系统借鉴 NASA 的安全关键代码《Power of 10》规则,启动后完全杜绝动态内存分配,并为循环、并发、网络及存储读写等一切关键资源设置显式上限与下限。 - [40:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2400s) 核心安全原则确立“正确性永远凌驾于可用性”,严格区分可恢复的运行期操作错误与未定义的代码缺陷,在生产构建中坚守断言并保留溢出检查,遭遇 Bug 宁可主动停机也不允许金融数据损毁。 - [46:08](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2768s) 基于 Zig 语言的确定性模拟器构建了加速时间的“时间机器”,在单进程内仿真网络分区与高达 30% 的存储损坏率,不仅前三周内捕获并清除了 30 个隐蔽分布式 Bug,还成功移植到浏览器 WASM 中进行可视化测试。 ## Key takeaways - [05:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=300s) 系统开发总成本由设计、编码、测试和生产故障共同决定,键盘打字速度只有边际收益,唯有源头设计才能实现数量级的降本增效。 - [10:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=600s) 推迟编码直至找到“全局极大值”架构,TigerBeetle 在动笔写第一行代码前将设计酝酿了三个半月,彻底消除了大量不必要的代码实现。 - [12:35](https://www.youtube.com/watch?v=w3WYdYyjek4&t=755s) 复杂存储系统的主要瓶颈在测试而非编码,传统黑盒测试被物理时间锁死,构建能够任意扭曲时间并百分之百复现现场的确定性模拟器是关键突破口。 - [15:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=900s) 拒绝拼装第三方依赖的“雕塑家”模式,选择从白纸起笔的“画家”路线,依托纯粹自研换取极限制约与完全可控的测试速度。 - [17:40](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1060s) 底层高性能系统可抽象为网络、磁盘、内存、CPU 四种资源与延迟、带宽两种质感,在设计期通过粗略估算即可规避绝大多数物理瓶颈与延迟尖刺。 - [20:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1200s) 初期必须明确定义网络、存储、内存和 CPU 的机械故障模型,收敛有状态组件并精简端口暴露面,降低系统认知复杂度。 - [22:36](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1356s) 推行“零技术债务政策”,意识到更好方案时立即停工重构,并通过离线踱步的“边走边谈”双人推演攻克棘手架构难题。 - [25:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1500s) 在函数的入口与出口建立严格的前后置断言契约,将系统打造为运行期自验证结构,坚守“要么安全运行,要么主动停机”的底线。 - [28:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1680s) 普通程序员局限于编译通过的“正向空间”,黑客则专攻非预期的“逆向空间”;分布式系统的混沌行为与异常条件天然具备黑客般的攻击性。 - [30:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1800s) 彻底摒弃无限 `while(true)` 循环,对执行次数设置显式的上下限断言;全系统推行启动期完全静态内存分配以杜绝资源浪费。 - [35:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2100s) 数千条断言在生产环境中全天候生效,充当测试的放大器,例如通过随机并发比对内存缓存与磁盘数据快速暴露存储不一致。 - [38:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2280s) 驳斥语言层面剥夺断言的做法,严厉批评混淆可恢复的“操作错误”与未定义的“编程缺陷(Bug)”的语言设计理念。 - [40:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2400s) 分布式环境自带进程副本容错,面对致命编程缺陷时迅速崩溃重启反而能打破死局并保留排查现场,带来更高的全局可用性。 - [43:24](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2604s) Zig 语言提供精细的底层内存控制,默认在安全构建中开启算术溢出检查,并支持用常规代码编写真正的编译期断言。 - [46:08](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2768s) 断言不仅是防御武器,更是展示系统负空间不变量的最佳文档,让新团队成员快速掌握系统严禁触碰的边界。 - [49:12](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2952s) TigerBeetle 利用依赖注入将状态机、网络、存储和时间源彻底解耦,在受控的单进程内存中实现完全可重放的集群级仿真。 - [50:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=3000s) VAPA 模拟器在读写路径疯狂注入高达 30% 的存储损坏与 34% 网络重放,自动校验并精准区分严格可串行化违背与活性阻塞问题。 - [52:43](https://www.youtube.com/watch?v=w3WYdYyjek4&t=3163s) 通过 Zig 将分布式数据库内核编译为 WASM,直接在浏览器中可视化展示 Viewstamped Replication(VSR)在网络分区与宇宙射线存储破坏下的自愈能力。 ## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=0s) 像魔术一样的开发风格:认识 TigerStyle 演讲伊始,Joran Dirk Greef 向全场抛出一个引子:他想教大家一个“魔术”。他让听众闭上眼睛,想象自己能想到的最困难的分布式系统问题或最前沿的软件项目——也许是一个共识协议(Consensus protocol)、一款数据库、一个操作系统,抑或是自动驾驶汽车、精密医疗设备等关键系统。如果以往构建这样的系统需要耗时整整十年,那么掌握了这个魔术后,仅需四到五年就能成功交付。这并不是投机取巧或减少努力,而是以更高的速度(Velocity)推进,打造出更优秀、更安全、更快速、运维更简单,且能让人在夜里安然入睡的坚固系统。 Joran 坦言,这套被称为“魔术”的开发风格,源自他在 Gerard Holzmann 和 James Cowling 等大师身上的学习所得,并在创建 TigerBeetle 的不到三年时间里付诸实践。TigerBeetle 是一款专为关键任务安全性和极限性能而打造的分布式金融记账数据库。这种开发模式常常被外人称作“疯狂”,但它展现出的运行状态却让人震撼得难以言表。Joran 希望通过这次分享改变大家对编程的认知,让那些人们过去不敢构想、不敢动手构建的系统,变得触手可及。他在开场中特别向 Will 以及 Antithesis 团队致敬,盛赞 Will 前一天的演讲是他听过最不可思议的分享,并预告了接下来的流程:讲解设计、构建与测试分布式系统的方法,现场演示该风格的威力,甚至还会带来一段 TigerBeetle 的“现场演出”。 这种开发风格被正式命名为 **TigerStyle**。Joran 直言,没有 TigerStyle,TigerBeetle 就不会存在,团队不可能仅用三年时间就构建出数据库、组建起 Tiger 团队并举办 Systems Distributed 大会。他特别向项目的联合创始人 Don 致敬——正是 Don 与他一同启动了项目,并构思了“TigerBeetle”这个名字。TigerStyle 的核心魔力在于“购买时间”并将未来的确定性提前,它由三个互相激发、相辅相成的核心理念驱动: 1. **纵观全局(Sees the whole picture)** 2. **防患未然(Checks yourself before you wreck yourself)** 3. **拥抱时光机(Gives you a time machine)** - “So TigerStyle is how we design, build, test Tiger Beetle for mission critical safety and performance. I can tell you that we wouldn't be here today at least without TigerStyle.” —— Joran Dirk Greef - “Because with tricks as powerful as these, sometimes seeing is believing.” —— Joran Dirk Greef --- ### [05:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=300s) 纵观全局:为什么慢编码反而带来高速度 Joran 假设在座的工程师都已经获得了“难题的馈赠”(The blessing of a hard problem)——拥有一个一旦解决就能为大众带来巨大价值的难题。但当面临难题时,人们往往陷入误区:盲目跟风技术潮流,诉诸权威,仅仅因为某个技术流行就引入 NPM 依赖、GraphQL 甚至 Rust。“你选用 Rust 应该是因为它的所有权检查器(Borrow Checker)和内存安全性极度出色,而不是因为别人都在用”,工具的选择必须基于质量而非流行度。 TigerStyle 打破了这种浮躁的技术选型方式。它要求工程师在动手写代码前保持克制、耐心等待;即便真正开始编写代码,节奏也是克制而缓慢的。Joran 将这种状态比作古代习武之人的克制修养(Restraint):虽然前期看起来极度磨人且放慢了脚步,但最终却能以极高的整体速度(Velocity)达成目标。原因在于,构建系统绝不仅仅是“编码”这一件事,而是由**设计(Design)**、**编码(Coding)**、**测试(Testing)**以及**生产运维与事故响应(Production incidents)**构成的全生命周期。工程师通常把全部精力聚焦在键盘打字上,但打字速度翻倍不过是 2x 的边际收益,每天可持续写代码的时间也有上限;唯有把视野拉远到全局,才能真正实现数量级的提速。 系统开发的总拥有成本(TCO)与时间开销,可以用一个函数关系来表达: $$\text{Time / Cost} = f(\text{Design}, \text{Coding}, \text{Testing}, \text{Production Incidents})$$ ```mermaid flowchart TD accTitle: 缺陷延后暴露的成本放大模型 accDescr: 展示设计阶段省略的工作如何在编码、测试和生产环境中呈指数级放大代价与时间。 subgraph 前期阶段["前期阶段(高杠杆)"] DES["深入设计 (Design)<br/>• 消除不必要代码<br/>• 确立静态分配架构"] end subgraph 实施与暴露阶段["后期暴露(高代价)"] CODE["克制编码 (Coding)"] TEST["系统测试 (Testing)"] PROD["生产环境事故 (Production Incidents)<br/>• 客户损失<br/>• 额外花费2-3个月修复并发布"] end DES -->|"精心设计可砍掉代码"| CODE CODE --> TEST TEST --> PROD PROD -.->|"线上发现Bug引发灾难性反噬"| DES ``` 在设计阶段保持“懒惰”是一种美德,因为优秀的设计能让你直接把某些代码“设计掉”,根本无须编写。反之,如果匆忙跳过设计阶段直接进入编码与测试,糟糕的设计就会在后期演变成灾难性的成本。一旦缺陷溜进生产环境,企业不仅要承担故障损失,而且为了评估对存量客户群的影响、制定方案、重新发布,往往要额外耗费两到三个月的时间去补救,代价呈指数级暴增。因此,在源头的设计上投入时间,正是 TigerStyle 纵观全局的第一准则。 - “So you should use Rust because Rust is amazing with the borrow checker, with memory safety, that's why you should use Rust, not because you see other people using it. Decide your tools on quality, not quantity.” —— Joran Dirk Greef - “So if we turn to the design phase, we have opportunities to speed up because the design informs what we code, and also what we don't have to code. So it's great to be lazy in the design phase, do things that you don't have to code. You can design code away.” —— Joran Dirk Greef - “So if you find a bug in production, it's disaster because now you've had damage. And to fix that bug is going to take you two or three months extra because you have to plan how is this going to impact all your customer base.” —— Joran Dirk Greef ### [10:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=600s) 设计阶段的推迟与全局最优(全局极大值) Joran Dirk Greef 警告工程师们不要操之过急,并引用《指环王》中的树人(Ents)作比喻:“出征时要像树人一样,成为一股不容小觑的力量,并且耐心等待,直到万事俱备。”在软件设计中,一小时甚至一天的精心构思,足以抵消生产环境中数周乃至数月的修修补补。许多工程师崇尚绝对客观,容易忽视“优雅”这种带有主观色彩的特质,但 Edsger W. Dijkstra 却完美地将优雅与工程执行结合在了一起。Dijkstra 指出,简单而优雅的系统不仅在设计与落实时更快、更容易,而且执行效率更高、可靠性也远超复杂系统。 由此引出的核心工程纪律就是**推迟编码**:在没有将设计大体敲定之前,坚决不动手写代码。开发者不需要立刻追求百分之百的完美,但必须确保自己正处于“全局极大值”(Global Maxima)的山坡上,而不是被困在局部最优的浅滩。如果数字对不上,或者直觉隐隐感到不安,就必须停下来等待,就像弗罗多和山姆站在末日火山脚下仰望一样,必须找到那座最庞大、最炽热的火山并针对它去攻克。对于 TigerBeetle 而言,团队在项目初期十分幸运地经历了这种等待:从 2020 年 3 月到 7 月,在动手写下第一行代码之前,整个设计构想被放置、酝酿(simmer)了整整三个半月;倘若没有这三个半月的沉淀,TigerBeetle 许多最核心的底层设计根本不可能诞生。 设计阶段虽然在整个工程周期中所占的时间比例极小,但其性价比却最高。在草稿纸上勾勒架构、推倒重来、与团队反复讨论排除致命缺陷(showstoppers)、评估性能直觉,其成本微乎其微;而一旦一头扎进代码导致走投无路,再想重写就要付出极高代价。在设计阶段每次迭代,方案的质量都会呈指数级提升。以一个为期两年的项目为例,在设计上多投入一个月,仅仅占总体开发时间的 4%,但正是这 4% 的前期投入,能够给后续的编码和测试阶段带来 10 倍、20 倍乃至上千倍的效率收益——Joran 直言,对于 TigerBeetle 而言,这个回报率就是实打实的 1000 倍。 - **Dijkstra 论优雅与执行**:“简单而优雅的系统往往更容易、更快设计和实现,执行更高效,也更加可靠。” - **推迟编码的原则**:“因此我认为,这种自律就是要推迟编码,耐心等待,直到设计大致成型。” - **TigerBeetle 的酝酿周期**:2020 年 3 月至 7 月(历时 3.5 个月)全用于纯粹的设计与推演,未写一行代码。 - **时间投资回报比**:两年期项目中多花一个月做设计仅占全流程的 4%,却能在后续阶段换取 10 倍到 1000 倍的效率飞跃。 --- ### [12:35](https://www.youtube.com/watch?v=w3WYdYyjek4&t=755s) 复杂系统的测试困局与“时间机器”愿想 在复杂系统工程中,真正占据主导地位、消耗最多精力的并不是编码,而是测试。对于简单软件,行业经验通常认为测试时间大致等同于编码时间(写 5 小时代码就测 5 小时,或者花一两天测试);但在复杂的分布式存储系统中,测试往往需要耗费数年甚至数十年。PostgreSQL 历经 30 年的演进,到了 2020 年社区依然在为 `fsync` 门事件(FsyncGate)善后。即使使用像 Jepsen 这样业界顶尖的分布式数据库测试工具,测试效率依然被物理时间牢牢锁死:一个需要运行一周才会触发的隐蔽并发 bug,就必须让 Jepsen 完整跑满一周;而如果想要复现 bug 或验证补丁是否生效,又得再跑一周,且这一切都建立在 bug 能够被百分之百复现的前提下。 常规测试手段的局限性暴露无遗。Joran 指出,通常构建一个像 TigerBeetle 这样的系统需要 2 年,但若要充分验证其共识与存储引擎,至少需要 5 年甚至 10 年。面对 2 年编写、10 年测试的漫长鸿沟,团队需要的是一种能“加速时间”的终极解决方案——一台能够在测试环境中任意扭曲时间轴、并且每次都能确定性复现 bug 的“时间机器”。如果能把长达数年的测试压缩到短短一年内完成,就会成为颠覆系统软件研发效率的“银弹”(Silver Bullet)。 ```mermaid flowchart TD accTitle: 传统测试与确定性时间机器测试对比 accDescr: 对比传统分布式测试等待时间漫长且复现困难,与时间机器加速并精确复现的差异 subgraph Traditional["传统测试模型(如 Jepsen)"] A1["偶发 Bug 潜伏期长"] --> B1["耗时运行 1 周触发"] B1 --> C1["尝试修复"] C1 --> D1["再次运行 1 周验证是否修复<br>(可能无法确定性复现)"] end subgraph TimeMachine["TigerBeetle 目标:时间机器模型"] A2["确定性模拟环境"] --> B2["虚拟时钟极速推进"] B2 --> C2["压缩 5-10 年测试至 1 年内"] C2 --> D2["100% 确定性复现与即时验证"] end ``` - **测试主导工程生命周期**:“测试——这只是我的观点,但我认为测试在复杂系统的开发中最具统治力。” - **物理时间限制**:在传统黑盒测试下,触发需要一周才能暴露的并发缺陷,每次循环就必须消耗真实的一周。 - **效率飞跃**:“如果构建 TigerBeetle 耗时两年,我们若能在一年内完成测试,而不是耗费四年甚至十年,那将是一颗真正的银弹。” --- ### [15:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=900s) 雕塑家与画家:软件设计的两种范式 这种通过前期深思熟虑来换取后期整体提速的策略,看似违反直觉,实则是“慢即是快”(Go slow to go fast)。Joran 认为,软件开发绝不仅仅是一门精密、冷酷的工程学,它兼具严格的逻辑与人类的创造力、灵感和艺术性。他特别引用了电影《创:战纪》(Tron: Legacy)中主人公 Kevin Flynn 所说的“生物数字爵士乐”(Bio-digital jazz)——严丝合缝的机器节奏(冷酷数据与硬核逻辑)与罕见的律动感(有机、鲜活的人性表达)彼此交融。 在软件艺术的形式选择上,Joran 借用了传奇音乐制作人 Daniel Lanois 的著名比喻:世上的音乐家分为两类,一类是“雕塑家”,一类是“画家”。 1. **雕塑家(以 Bob Marley 为代表)**:起手面对的是一堵现成、厚重的大理石声音之墙(Wall of sound),然后一锤一凿地剥离、敲打、剔除多余部分,直到将其精炼为雷鬼乐(Reggae)那种充满呼吸感与留白的声音结构。 2. **画家(以 The Beatles 为代表)**:起手面对的是一张纯白的画纸,甚至只是一间空浴缸和几根铁链;他们从无到有地构图、描线,一层层泼洒色彩、涂抹油彩,最终精准抵达他们预想的彼岸。“画家从不迷失,总是能准确到达他们心之所向。” 当今软件行业的大多数项目都在采用“雕塑家”模式:引入堆积如山的外部开源依赖库,如同搬进一块块沉重大理石,随后在上面缝缝补补、削切拼装。然而在设计 TigerBeetle 时,团队意识到绝不能走雕塑路线——大量的第三方依赖会彻底拖垮测试速度,并引入不可控的安全隐患和性能抖动。TigerBeetle 必须是一张从白纸开始绘制的画作,唯有亲手“绘制”,才可能诞生出像披头士的《Strawberry Fields Forever》那样浑然天成的作品。 - **软件的本质**:“如果你相信软件不仅是工程,更超越了工程,软件中既有精确度,亦有艺术性;既有硬核数据、冷峻逻辑,也有人们所赋予的人类经验、创造力、信息与灵感。” - **雕塑式开发的陷阱**:现代开发过度依赖现成轮子,导致依赖黑盒庞大,使得后期的确定性测试与极限制约近乎不可能实现。 - **团队风格选择**:TigerBeetle 团队定位为“画家”,从零开始铺展每一行逻辑,正是这种自研纯粹性构成了团队极致开发速度的源泉。 --- ### [17:40](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1060s) 画布上的四种颜色与两种质感 明确了“画家从虚无中作画”的范式后,系统设计就简化为了选择画布上的颜料。Joran 指出,构建底层高性能系统其实只需要掌握四种基础“颜色”以及两种通用的“质感”: | 维度 | 系统元素 | 核心特性 | |:--- |:--- |:--- | | **四种颜色(基础资源)** | 网络(Network)<br>磁盘(Disk)<br>内存(Memory)<br>CPU | 构建任何软硬件系统的基石;底层资源在行为模式上具备对称性与相似性。 | | **两种质感(性能属性)** | 延迟(Latency)<br>带宽(Bandwidth) | 四种资源共同遵循的物理约束;一切系统建模与调优都在平衡这两个指标。 | 只要掌握了在磁盘与内存之间绘制系统的能力,处理网络与 CPU 便遵从完全相同的法则。在架构设计阶段,工程师应当使用这四种颜色和两种质感,在草稿纸上进行详尽的粗略估算(Back-of-the-envelope calculations)。这一计算能够快速描摹出系统的整体形态,在尚未动笔写代码前就提前验证系统在物理硬件上是否可行,并从根源上杜绝潜在的延迟尖刺(Latency spikes)。 Joran 强调,在设计期应尽可能多地草拟出各种不同的解决方案分支,因为“草图成本极低,重写代价高昂”。此外,他还提醒开发者务必在命名上下足功夫,花时间为组件、函数和变量推敲出贴切精确的名称。在计算机科学中,虽然命名常被称为“最难的两件事之一”,但 TigerBeetle 在其开源仓库的 `TigerStyle` 风格规范文档中,专门针对这一被称为“第二难”的命名问题给出了详尽的实操建议与技巧指南。 - **色彩与质感的统一**:“网络、磁盘、内存、CPU。这就是全部的四种颜色;而所有这些颜料所共享的两种质感,就是延迟和带宽。” - **草图优于重写**:“在设计阶段尽可能多地绘制不同方案的草图,因为草图非常廉价,而重写代价昂贵。” - **TigerStyle 规范**:TigerBeetle 仓库中的 `TigerStyle` 指南详细总结了系统资源核算方法与代码命名的实战策略。 ### [20:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1200s) 明确故障模型与削减系统暴露面 Joran Dirk Greef 探讨了命名规范对心理模型(mental model)的深远影响。他开玩笑提到社区现在甚至要求在 Zig 代码规范中引入 snake_case,但他强调核心目标只有一个:必须把名词和动词用得恰到好处。精准的名词与动词意味着开发者真正理解系统并建立了清晰的心智模型,正如 Edsger W. Dijkstra 所揭示的,只要心智模型清晰,后续一切都会变得简单。人类自身存在“故障模型”(fault model),容易犯错,而良好的命名能大幅降低沟通失误的几率。 人类有缺陷,机器同样如此。因此在架构设计之初,就必须明确定义底层的机械故障模型(mechanical fault models)——涵盖网络、存储、内存和 CPU 故障模型。在 TigerBeetle 这类项目中,必须假定内存分配随时可能失败并安全地处理它,作为数据库绝对不能直接 panic;在其他超大规模系统中,甚至连 ECC 内存都不可靠,需要将 CPU 计算错误纳入考量,并引入拜占庭容错(Byzantine fault tolerance)。核心准则是:在设计初期就要清晰断言系统应当容忍哪些故障,从而通过针对性的设计从源头上减少故障暴露面。比如若存储模型假定磁盘可能会变慢或发生静默数据损坏(bit rot),设计上就要尽可能让更多组件脱离磁盘。无状态(stateless)永远优于有状态(stateful),状态的管理极度困难,必须严格限制有状态组件的范围。 ```mermaid flowchart TD accTitle: 故障模型定义与架构暴露面收敛流程 accDescr: 展示从定义硬件故障模型到无状态化隔离以及减少系统暴露面的架构收敛路径 subgraph Faults["定义底层故障模型 (Fault Models)"] F1["网络与存储故障<br/>(延迟、位衰减 bit rot)"] F2["内存与 CPU 故障<br/>(分配失败、计算错误)"] end subgraph Architecture["架构设计规避"] A1["关键状态严格隔离<br/>(TigerBeetle 核心状态机)"] A2["其余组件无状态化<br/>(Stateless 优于 Stateful)"] end subgraph Surface["收敛暴露面 (Surface Area)"] S1["减少组件数量<br/>(从 200 个精简至个位数)"] S2["收敛外部暴露端口<br/>(单进程仅使用单一端口)"] end Faults --> Architecture Architecture --> Surface ``` 系统暴露的面越大,无论是有状态还是无状态组件,意料之外的交互和维护负担就会呈指数级增加。Joran 举例指出,拥有 200 个组件的系统人类大脑根本无法装下,而 3 个组件就很容易掌握;再如进程通信,许多系统喜欢为一个进程开启多个网络端口,而 TigerBeetle 倾向于每个进程仅开一个端口。更少的端口意味着更小的暴露面、更简洁的部署流程和更高的运行效率。减少暴露面并不等于偷工减料,而是为了达到“尽可能简单,但不能再简单”。 - 机械故障模型必须在项目初期就明确制定,包括网络、存储、内存和 CPU 四个维度。 - “无状态永远优于有状态;管理状态非常困难,状态的本质就是在造一个数据库。” - Joran Dirk Greef 指出:“如果编译通过了,你才刚刚起步。”(“If it compiles, you've just begun.”) --- ### [22:36](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1356s) 零技术债务政策与边走边谈(Walk and Talk) 谈及系统暴露面的收敛,Joran 介绍了 TigerBeetle 团队内部著名的“零技术债务政策”(Zero Technical Debt Policy)。他打趣说不知道团队里谁在扮演甘道夫的角色,大概是 Adrian 戴着那顶坚守原则的甘道夫帽子。这一政策的实质并非代码永远完美无瑕,而是在当下尽团队所知做到最好:如果在编码过程中意识到有更优雅、更稳妥的设计方案,绝不推诿延后,必须立刻停下手中的工作,改用更好的方式实现。“第一次就把它做对”,因为日后回过头来修复的成本极高——大脑需要重新加载庞大的系统上下文,耗时费力,而且未来往往再也没有机会回过头来弥补遗憾。这种把工作做扎实的笃定感,能带来强大的团队正向激励与研发动能。 为了激发这种工程创造力并解决棘手的架构难题,Joran 分享了他最推崇的方法:双周举行一次的“边走边谈”(walk and talk)。团队成员在远程连线讨论设计时,绝不僵坐在电脑前。比如他身处开普敦办公室的会议室里连续踱步一个小时,而同在开普敦的同事 Fed 则在办公室走廊和阳台之间走动(甚至偶尔走到阳台信号中断,被催促赶紧回室内)。大家关掉摄像头,戴上蓝牙耳机,完全专注于口头推演。两人轮流抛出解决方案,再互相提出反例去攻破它。许多绝妙的突破往往就发生在来回踱步探讨一小时后,某人突然顿悟:原来这整套繁琐的问题,可以通过顶层设计直接消除掉。 > Joran Dirk Greef 强调了第一次就做对的重要性:“在 TigerBeetle,我们推行零技术债务政策……我们之所以在第一次就把事情做对,是因为日后再回过头来代价极其高昂,要把上下文重新加载进大脑需要耗费大量时间,而且谁能保证以后真的还会有机会去重构它呢?”(“So on this note, we have a zero technical debt policy at Tiger Beetle... We do it the first time because it's important because it's expensive later to come back, you know, to reload stuff into your head. It takes time to get back into the system. And who can be sure that they ever will?”) --- ### [25:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1500s) 断言即陷阱:从防御式编程到自验证系统 经过深思熟虑的架构设计后,演讲进入了核心的编码阶段。Joran 坦言:“TigerStyle 确实会让你变慢,我向你保证这一点;但它慢下来的这一步,会为后续的所有阶段大幅加速,并带来极高标准的代码质量。”无论代码审查多么严苛、自动化测试多么完备,软件系统中总会有如同超级马里奥(Super Mario)穿墙跳上天花板那样的幽灵 bug 溜过去。面对这种必然性,系统最底层的防御底线必须是:**要么在安全参数内正确运行,要么在检测到可能存在 bug 的瞬间立即主动崩溃停机**。系统向用户做出的最高保证就是“要么安全运行,要么不运行”。 为了落实这一保障,TigerBeetle 借鉴了 Gerard Holzmann 为 NASA 编写的《安全关键代码的十大规则》(Power of 10 Rules for Safety-Critical Code),在全系统广泛布设“断言”(assertions)。Joran 将断言形象地比作战场上的“绊线”(tripwires)——料想到未来有人重构代码时可能会调换分支顺序,便在关键路径上安插一根绊线,一旦执行时序异常便立刻“轰”地引爆并终止程序。团队成员 Jamie 在提交代码供审查时甚至曾扬言要故意引入异常,看审查者能否察觉绊线。 在函数实现上,断言不仅仅是抽象的工具,其终极目标是保证正确性: ```mermaid flowchart TD accTitle: 函数级契约式断言与状态自验证 accDescr: 展示函数入口参数断言、执行逻辑与出口返回值断言构成的自验证系统闭环 subgraph FunctionEntry["函数入口 (Input / Preconditions)"] A["传入参数列表"] --> B["断言: 参数值合法性边界"] B --> C["断言: 参数之间的关联性约束"] C --> D["断言: 输入与当前系统全局状态的一致性"] end subgraph Execution["核心逻辑执行"] D --> E["无副作用/确定性状态计算"] end subgraph FunctionExit["函数出口 (Output / Postconditions)"] E --> F["断言: 返回值满足业务不变量"] F --> G["断言: 计算后全局状态演进的正确性"] G --> H["安全返回给调用方"] end ``` 这种在每个函数的入口和出口对输入、输出及系统全局状态关系进行的双重断言,让整个程序变成了能够在未知环境与异常数据下实现“自验证”(self-verifying)的坚固结构。 > Joran 强调断言的本质:“在花费数天做代码审查、数周做测试之后,我们还剩下什么防线?我认为最好的防线就是构建你的系统,使其要么正确运行并在安全参数内运作,要么在检测到可能存在缺陷时立刻停机。”(“And I think here the best defense is to build your system so that you either run correctly that you operate within safe parameters or else you shut down if you detect that a bug might be present.”) --- ### [28:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1680s) 程序员的正向空间与黑客的逆向思维 在剖析断言机制的深层逻辑时,Joran 指出断言表面上是在表达“你对代码执行的期望”,但这只揭示了硬币的一面。这也是普通程序员与真正的“黑客”(hacker,例如 TigerBeetle 团队的 Will)之间的根本思维分水岭。 普通程序员往往缺乏完全成型且经过硬化检验的心智模型,他们的思维停留在“正向空间”(positive space)——只想让代码跑起来,只要代码能够通过编译器编译,就误以为程序已经是正确的。然而在系统级编程(systems programming)中,“一旦编译通过,一切才刚刚起步”。相反,黑客天生工作在“逆向空间”(negative space):他们绝不走正门,而是专门去寻找和攻击编译器以及开发者认知盲区中的非预期逻辑。就像超级马里奥的开发者从未预料到玩家能卡进天花板跳关一样,现实世界中总会发生开发者意想不到的离奇路径。而在复杂且不可预测的分布式系统环境中,所有节点、网络分区和并发条件,本质上都充斥着这种具有破坏性的逆向行为。 - 普通程序员的心智陷阱:误把“编译通过”等同于“系统正确”,仅在正向空间内编写符合预期的 happy path。 - 黑客思维方式:专攻逆向空间与边界盲区,从系统如何崩溃、逻辑如何被违背的角度审视系统状态。 - 分布式系统的残酷现实:在高度并行的分布式集群中,环境本身的混沌行为与不断出现的异常条件,其攻击性丝毫不亚于全天候寻找漏洞的黑客。 ### [30:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1800s) 像黑客一样思考:为循环与一切资源设定显式上下限 系统所处的外部环境极其复杂,不仅随时会向系统发起攻击,还会在生产环境中制造出最意料之外的极端场景。工程师面对黑客时,黑客总能带来各种意想不到的“惊喜”。因此,设计系统时绝不能仅仅考虑“预期之内”的情况,而必须开始像黑客一样思考:“什么是你未曾预料到的?什么情况会极其反常或出人意料?”唯有搞清这些,才能看清系统真正的边界到底在哪里,并对其进行断言。如果对边界的理解过于严苛,系统便会触发断言崩溃,这恰好提供了一个修正认知、学习并摸清系统“盒子外形”的机会。 一个非常典型的反思是代码中的 `while (true)` 循环。在编写解析数据的循环时,没有人预期它会跑上一整个小时或者陷入死循环。Joran 向台下提问:“你们写 `while (true)` 循环时,有没有考虑过它到底应该运行多久?有人举手吗?”见台下有人举手,他调侃道:“写过 `while (true)`?很好,干得漂亮。但我自己很难做到时刻警醒自己——天哪,我刚写了个 `while (true)`,这太危险了。难道我指望它运行到地老天荒吗?这会消耗巨额算力。” ```mermaid flowchart TD accTitle: 循环上下限与资源限制机制 accDescr: 展示在系统设计中通过限制循环次数和资源使用来保障系统稳定性的流程 subgraph LoopBounds["循环边界防御"] A["常规循环设计"] --> B{"编写为有限 for 循环"} B --> C["断言上限: 防止失控死循环"] B --> D["断言下限: 像黑客一样验证最低执行次数"] end subgraph SystemLimits["系统全局限制"] E["内存 / 磁盘 / 网络 / CPU / 并发"] E --> F["启动后完全静态分配"] E --> G["Google SRE 第8章: 明确过载边界与反压"] end C --> H["系统具有确定形状与高韧性"] D --> H F --> H G --> H ``` 为了防止生产环境中出现排查极其困难的失控死循环与死锁,必须使用带有固定上限的有限 `for` 循环。然而限制上限只是解决了一半的问题,真正的“黑客思维”要求工程师必须同时对下限进行断言:期望循环至少执行若干次。上下限的约束直接引出核心原则:当今太多系统的设计就像 90 年代 2 Unlimited 乐队的那首经典老歌《No Limit》(没有极限)唱的那样,全然不设边界。 Joran 严肃指出,无边界的系统根本无法被理解:“系统是有极限的,你最好把这些限制显式表达出来,这样你的设计才能处理所有的边缘情况。否则,你根本没有真正理解你的系统。它甚至都不是一个四角方正的黑盒,而是一个软趴趴、不可捉摸的肉团,因为没有任何限制。” 借鉴 NASA 为安全关键代码制定的《Power of 10》规则,一切资源都必须设限: - **静态内存分配**:TigerBeetle 在启动完成后绝不进行任何动态内存分配。团队预先计算了共识协议各种状态排列组合所需的所有内存(如预分配的消息缓冲区)、存储引擎做压缩(compaction)所需的最大内存,以及所有并发操作的内存上限。 - **全方位受限**:循环有上限,内存、网络、磁盘、CPU 以及磁盘与网络的并发操作全部具备显式限制。 - **过载保护与反压**:正如《Google SRE》第 8 章“应对过载”所强调的,系统只有知晓自身能力的绝对上限,才能在面对超载时正确实施背压(backpressure),打造出坚如磐石的生产系统。 --- ### [35:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2100s) 限制带来的极致精炼与生产环境断言的力量 将所有资源限制明确后,系统最终会变得极其精炼强悍(lean and mean)。由于在资源边界上投入了极致的专注,系统不会产生任何资源浪费,内存效率甚至能比以往提升 10 倍以上。静态内存分配非但不会撑大内存占用,反而因为杜绝了浪费而使用更少内存。虽然这种编码与构建阶段需要耗费更多时间,但一旦边界确立,工程师便可以对这些限制进行断言与充分测试,从而在后续开发中建立起远超其他系统的“测试速度”(test velocity)。 断言是测试的“力量倍增器”(force multiplier)。目前 TigerBeetle 代码库中拥有数千条断言,每一次运行 TigerBeetle,这数千条断言都在生产环境中持续生效并严格把关。Joran 分享了一个生动的实际案例:缓存是计算机科学中最棘手的问题之一,极易出错。TigerBeetle 在从缓存读取数据块时,有时会同时从磁盘中读取该数据块进行比对断言,确保没有发生 fsync 遗漏,缓存数据与磁盘真实数据完全一致。 “几周前,在我们进行基准测试时,这个断言在一个罕见的负载下触发了失败,我们立刻意识到出问题了。Jamie 花了 8 个小时进行排查调试,并在他的博客上专门写了一篇复盘文章,读起来就像一部福尔摩斯侦探小说。这是因为如果没有这个断言,这个 bug 可能要在生产环境中潜伏数月甚至数年才会被发现,并带来灾难性的后果。”尽管连测试覆盖都没能提前揪出该边缘问题,但基准测试运行时的断言立刻捕获了它;花 8 个小时定位修复,比起线上爆发后排查数月乃至数年,已经是巨大的胜利。 - **断言的规模效益**:TigerBeetle 拥有超过 3000 条内部断言,在生产中全天候守护运行。 - **防御性一致性检查**:随机并发对比 Cache 与 Disk 内容,用极小代价换取底层存储不一致问题的光速暴露。 --- ### [38:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2280s) 语言设计对断言的误解:混淆可恢复操作错误与致命编程缺陷 既然断言具有如此强大的威力,为何当今绝大多数代码库却很少使用,甚至编写函数时从不校验输入和输出?Joran 认为问题的根源出在编程语言层面。图灵奖得主 Barbara Liskov 曾指出,若想向程序员传授新的思维方式,必须给他们提供用于思考的新编程语言。然而残酷的现实是,许多现代语言要么在语言层面剥夺了断言,要么在生产安全构建(safe builds)中默认关闭断言。 这种做法极其讽刺,因为生产环境恰恰是 bug 造成破坏力最大的地方。行业现状却是:一旦进入生产环境,人们就把所有的安全检查全都关掉。以现代系统级语言 Go 为例,Go 语言在很多方面非常出色,TigerBeetle 团队也在运维工具和 Workshop 中愉快地使用 Go。然而在 Go 语言官方的 FAQ 中,官方解释了不提供断言的原因:虽然断言不可否认地方便,但 Go 团队的经验认为程序员会把断言当成拐杖,从而回避去认真思考妥善的错误处理与错误报告;妥善的错误处理能让服务器在遇到非致命错误后继续运行,而不是直接崩溃。 ```mermaid flowchart LR accTitle: 操作错误与编程缺陷的区别 accDescr: 阐明系统运行中应被处理的操作错误与应被断言暴露的编程缺陷的区别 subgraph ErrorTypes["错误本质区分"] direction TB OE["操作错误 / 运行期环境错误<br>(Operating Errors)"] PE["编程错误 / 代码缺陷<br>(Programming Errors / Bugs)"] end subgraph HandlingStrategy["正确应对策略"] direction TB H1["妥善错误处理与反压<br>(例如: 内存紧张时拒绝新连接)"] H2["断言直接崩溃<br>(Crash Early, 暴露非预期状态)"] end OE --> H1 PE --> H2 ``` Joran 严肃反驳了这种语言设计哲学: > “断言,当你真正理解它们的时候,就会明白这根本不是为了图方便。它是关于正确性,它是任务关键的(mission critical)。诚然,有些程序员可能会滥用断言来代替常规的错误处理,但这绝不能推导我们在语言层面被剥夺使用断言的权利。最重要的是,这种回答将‘操作错误’与‘编程错误(Bug)’混为一谈——操作错误是你能够处理的错误,而编程错误则是你根本无法处理的错误。” 举例来说,如果服务器遭遇内存紧张,正确的做法是将其作为操作错误来处理:当 `malloc` 返回错误时,系统应当拒绝接受更多外部网络连接,实施反压以实现自我保护;绝对不能把这种情况直接搞成全局 panic 导致进程崩溃。然而对于代码逻辑层面的编程缺陷,断言崩溃才是保障数据不被破坏的唯一正确手段。 ### [40:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2400s) 编程错误无法被“处理”:为什么安全关机才是唯一的出路 面对操作错误(Operating Error)与编程错误(Programming Error)的界限,Joran Dirk Greef 明确指出,断言绝不能用来处理常规的运行环境错误,断言的唯一职责就是捕获糟糕的代码。开发者常常会产生一种天真的执念:既然我能检测到编程错误,为什么我不去提前处理它?为什么不预先想好所有的 bug 并写逻辑把它们“接住”?对此,Joran 给出了极为犀利的回答:“The answer is that if you can predict a bug to handle it correctly, why not fix it in the first place? You know, by definition, bugs can't be predicted. There is no proper handling. You're into undefined logic territory. The safe thing to do is shut down safely. You can't continue to operate.”(答案是,如果你能预知一个 bug 并正确地处理它,为什么不一开始就把它修复呢?顾名思义,bug 是无法被预测的。根本不存在什么所谓的妥当处理。你已经进入了未定义逻辑的领域。最安全的做法就是安全地关机。你不能继续运行下去了。) 随后他直面了工业界最常见的质疑:在一个要求高可用的数据库里,你怎么敢随便写断言让它崩溃退出?Joran 坚定地认为,正确性永远凌驾于可用性之上。停机固然有成本,但不停机造成的代价要高昂得多。他打了个比方:如果你已经明确知道系统下一秒就要开始以每秒数万笔的速度损坏金融交易数据,你真的会说“千万别停机,我们必须维持系统运行”吗?此时的问题根本不是“要不要关机”,而是“我们能以多快的速度关机”。无论手动还是自动触发停机,这往往是决定一家公司继续生存还是直接倒闭的分水岭。断言正是将这种自毁止血机制自动化的绝佳武器——一旦踏入有 bug 的代码分支,立刻崩溃关机。他特别告诫大家:永远不要在生产环境中关闭断言,也绝不要让你的编程语言在生产编译时自作聪明地把断言优化掉。 更反直觉的是,在分布式系统中,崩溃反而能带来更高的可用性。分布式系统本身就内置了容错机制,以集群的形式同时运行着同一个程序的多个进程副本,它们在设计之初就做好了部分进程崩溃并自动重启的预期。因此,断言在关键任务场景中能够发挥奇效:通过崩溃,它能把陷入异常状态的有故障进程强行拉出死局。一旦重启,由于触发 bug 的条件极其罕见,该节点大概率不会立刻再次踩坑,从而使服务迅速恢复正常;同时,崩溃留下的堆栈信息给工程团队提供了直观的可视性,帮助他们彻底铲除系统中的潜在隐患。 ```mermaid flowchart TD accTitle: Assertion Crash vs Corrupted State accDescr: 流程图展示断言捕获代码 bug 触发安全崩溃与忽略 bug 继续运行的后果对比 subgraph BugDetected["代码遇到未知 Bug (编程错误)"] A["进入未定义逻辑状态 (Undefined Territory)"] end subgraph TigerStylePath["TigerStyle:触发断言安全崩溃"] B["Assert 失败立即 Crash 退出"] C["防止金融数据进一步损毁"] D["集群副本接管 / 进程自动重启"] E["暴露故障现场,彻底定位并修复 Bug"] end subgraph DangerousPath["错误做法:掩盖错误勉强运行"] F["尝试吞掉异常继续提供服务"] G["每秒破坏数万笔财务事务"] H["产生静默数据损坏与不可逆商业灾难"] end A -->|"断言拦截"| B B --> C C --> D D --> E A -->|"忽略断言 / 无防御"| F F --> G G --> H ``` ### [43:24](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2604s) Zig 语言的极致安全哲学:检查算术与编译期断言 谈到工具的选择,Joran 盛赞了 Zig 语言在数据库开发领域的统治力。数据库本质上就是操控内存的艺术,而 Zig 提供了他所见过最丰富、最锋利的底层内存控制工具。更重要的是,Zig 从各个维度鼓励开发者以正确的方式使用断言。例如,在进行加法或减法运算时,Zig 编译器会自动在底层布设警报陷阱——正如 Joran 开玩笑说的那样:“Andrew 就是这种性格,对吧?我们得在这儿埋个绊线。”一旦算术发生溢出,程序会立刻触发断言失败并打印出清晰的调用栈追踪(stack trace)。在 Zig 的安全构建模式(Safe Builds)下,这种算术检查(checked arithmetic)是默认开启的。 Joran 情绪激动地呼吁:“If safety is important, you cannot disable checked arithmetic in safe builds. It is madness to do that, right? But yet languages do, and it boggles my mind.”(如果安全性很重要,你就绝对不能在安全构建中禁用检查算术。那样做简直是疯了,对吧?然而很多语言偏偏就这么干,这真让我匪夷所思。)他由衷希望所有语言未来都能在安全模式下默认开启溢出检查。当然,如果有开发者在完成了 100% 测试覆盖的数据面(data plane)极度追求极致性能,Zig 也允许通过代码块注解显式选择关闭断言,但这必须是主动的、审慎的选择,绝非系统默认的隐式妥协。 除了运行时的安全保障,Zig 还将断言推向了全新的高度:利用其实际的常规代码能力实现真正的编译期断言(Compile-time Assertions)。这不是黑魔法式的文本宏展开,而是货真价实的强类型代码逻辑。这使得工程团队在程序真正运行之前,就能在编译期完成整个程序架构设计的完整性验证。正如 Joran 所言,在编译期崩溃远远好于在 CI 自动化测试中崩溃,而在 CI 中崩溃又远远好于在生产环境崩溃。这种系统性的正确性思考方式,正是 TigerBeetle 最终坚定选择 Zig 的核心原因。他向在场的 Zig 创始人 Andrew Kelley 表达了极高的敬意,坚信 Zig 将成为未来二三十年席卷整个行业的系统编程风暴,其在游戏开发领域的爆发就是硬核系统工程趋势的最佳试金石。 ### [46:08](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2768s) 负空间的教学意义与 Tigersyle 的确定性“时间机器” 在进入现场实操演示之前,Joran 阐述了断言带来的深层次次级效应:断言是绝佳的代码文档,它明确记录了系统的各项不变量(Invariants),从而在团队规模扩张时大幅提升交付速度。当新成员阅读带有详尽断言的代码时,不仅能看到正向的业务逻辑(Positive Space),更能通过一个个绊线断言看清绝不允许发生的“负空间”(Negative Space)。“Can you just imagine? I mean, most code is not written like that. But that is what teachers do. They want to teach you the right way, and they also want to teach you the wrong way. Then you really understand something.”(你能想象吗?我是说,大多数代码都不是这么写的。但这正是好老师的教学方式。他们想教你正确的做法,也想教你错误的反面。只有这样,你才能真正理解某件事物。)Joran 感慨自己写了 28 年代码,非常遗憾直到最近 7 年才真正领悟断言的威力,开玩笑说如果早点明白这一点自己大概能年轻 20 岁。 所幸,TigerStyle 体系赋予了他们一台能加速时间的“时间机器”——TigerBeetle 的确定性模拟器(Deterministic Simulator)。为了将故障测试推向理论的物理极限,团队在模拟器中不仅模拟了各个 TigerBeetle 副本之间的网络环境,还完整模拟了存储介质。在这个模拟器里,他们会故意同时损坏所有副本的底层磁盘,并疯狂注入各种随机的网络丢包、乱序和极端存储延迟。在如此惨烈的混沌测试下,系统必须始终保持最严苛的严格可串行化(Strict Serializability)一致性,且绝不允许出现任何断言失败。一旦出现不变量被打破或断言失败,他们可以在短短几秒内使用固定的随机种子完全重现(Replay)故障现场,极速定位排错。 - 故障注入极限:在单次模拟中同时腐蚀整个集群的所有物理磁盘,注入随机网络分区与异常存储延迟。 - 研发加速度:模拟器刚上线运行的前三周内,团队就以每天分泌肾上腺素的节奏,连续捕获并彻底修复了 30 个极其隐蔽、难以捉摸的复杂分布式 Bug。 - 隐性收益:Joran 直言,如果没有这套确定性模拟器,这些灾难性 Bug 若散落到生产环境中,恐怕需要数百名工程师耗费数年时间才能排查清理干净,其间造成的经济损失不可估量。 ### [49:12](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2952s) 演示:在单进程内存中运行的分布式集群仿真 为了证明这一切不是纸上谈兵的魔术,Joran 现场切入终端展示了官方仓库中的 `simulator.zig` 代码。尽管他调侃这只是黑底白字的命令行文本,远没有看超级马里奥通关那么花哨炫目,但其中所展现的架构力量却极其震撼。 通过 Zig 强大的编译期元编程能力,TigerBeetle 构建出了一个完全可配置的分布式数据库类型。在这个编译期构建过程中,所有核心组件均实现了完全的依赖注入与可插拔设计: 1. **状态机(State Machine)**:直接传入核心业务逻辑; 2. **消息总线(Message Bus)**:支持随意替换节点间的网络通信机制; 3. **存储后端(Storage Backend)**:可任意插拔与模拟底层读写驱动; 4. **时间源(Time Source)**:能够随心所欲地快进、暂停或加速时间流动。 借助这套机制,编译器最终拼装生成出分布式数据库的副本(Replica)。在模拟测试中,他们将原本需要跨多台机器部署的数个副本完全放置在单个进程的内存中运行,节点间通过带有延迟模拟的网络仿真层相互通信,整个分布式集群在受控的单进程环境中完美运转。所有的测试执行完全绑定于一个随机种子(Random Seed),一旦产生任何偏差,只需输入相同的种子,就能在几秒钟内实现毫秒级精度的确定性重放与排查。 ### [50:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=3000s) 确定性故障注入模拟器与 VAPA 演示 Joran Dirk Greef 详细介绍了 TigerBeetle 的测试仿真环境。在网络层面上,开发者可以精确指定网络拓扑特征:配置特定的延迟分布、丢包策略、网络分区,甚至可以构造节点间不对称的带宽和延迟(即同一个链路中,A 节点向 B 节点发送数据极慢,而反向却很正常)。但 TigerBeetle 更进一步,深入到了读写路径上的存储错误注入。Joran 指出:“This is very, I don't know of other systems that really do storage fault corruption on the read and write paths.” 在很多场景中,TigerBeetle 会毫不客气地注入高达 30% 的损坏率——无论是读盘还是写盘,直接将数据写到错误的位置或返回损坏数据,同时还会随机使节点(Beetle)崩溃。 随后,Joran 在现场运行了名为 VAPA(Viewstamped Operation Replicator)的模拟器,其名字致敬了电影《战争游戏》(*War Games*)中的战争模拟器 WOPR。在当前的仿真中,系统基于一个随机种子(Seed)初始化了 3 个副本节点(Replica)组成的集群,并由 27 个并发客户端持续发送请求。测试中网络消息有 34% 的概率被重放以高频验证幂等性,同时读写分别设置了 3% 的错误损坏率。模拟器拥有对全局状态的完全可见性,能够追踪每一次状态机迁移并验证严格可串行化(Strict Serializability)。随着副本 0 发生故障,系统自动完成领导者选举,副本 2 成为新 Leader 并推进状态。该模拟器不仅能验证系统是否通过测试,还能在出现异常时精准定性属于共识违规(Consensus Violation),还是仅属于活性问题(Liveness Issue)或死锁(Deadlock)。 ```mermaid flowchart TD accTitle: TigerBeetle VAPA 模拟器运行架构 accDescr: 展示全局确定性模拟器如何驱动 27 个客户端向 3 个副本注入网络与磁盘故障并检测一致性 subgraph Sim["VAPA 全局确定性模拟器"] Seed["随机种子 (Seed) / 全局状态检测"] FaultNet["网络故障注入 (34% 重放 / 分区 / 延迟)"] FaultDisk["存储故障注入 (3% 读损坏 / 3% 写错位)"] end subgraph Cluster["TigerBeetle 集群 (3 Replicas)"] R0["Replica 0 (故障/下线)"] R1["Replica 1 (Follower)"] R2["Replica 2 (新 Leader, 状态推进)"] end Clients["27 个并发客户端"] -->|"发送请求"| Cluster Sim -.->|"注入故障并监控状态迁移"| Cluster Cluster -->|"严格可串行化校验"| Seed ``` - 集群拓扑配置:3 个副本节点构成单一逻辑数据库,承接 27 个客户端的并发流量。 - 故障注入参数:34% 网络消息重放概率、3% 读损坏率、3% 写损坏率,外加受控的节点崩溃与视图变更。 - 自动故障归类:能够自动区分共识破坏与活性阻塞/死锁问题。 ### [52:43](https://www.youtube.com/watch?v=w3WYdYyjek4&t=3163s) 浏览器端 WASM 游戏化展示:City Breeze 为了进一步展现确定性仿真与 TigerStyle 的工程红利,Joran 展示了一个内部的“臭鼬工厂”(Skunk Works)项目——基于浏览器 WebAssembly(WASM)运行的 TigerBeetle 游戏化集群仿真平台。该项目源自 Joran 前一年 4 月在米兰与 Fabio 的结识,并由 Joey 与 Fabio 共同推进。得益于 Zig 语言对 WASM 的编译支持,完整的分布式数据库核心逻辑能够直接跑在浏览器中。Joran 现场准备了三张“游戏卡带”:City Breeze(城市微风)、Red Desert(红土荒漠)和 Radioactive(辐射地带)。 第一关是名为“City Breeze”的理想化场景。Joran 幽默地调侃道:“This is how we write our distributed systems. Everything is perfect, right? Because we don't expect anything.” 在这个关卡中,系统处于绝对无故障的最优工况:没有丢包、没有延迟抖动、零存储损坏、节点永不崩溃。伴随着激昂的电子音乐,甲虫们(Beetles)平稳落地,在浏览器内顺畅执行 Viewstamped Replication(VSR)的 Prepare、Backup 和 OK 流程。Joran 笑着说这种状态对测试而言“太简单了”(Too easy),必须立刻升级难度。 - 技术栈:基于 Zig 语言将原生 TigerBeetle 数据库代码编译为 WASM,直接在浏览器端运行真实集群。 - 游戏卡带架构:分为 City Breeze(无故障理想态)、Red Desert(网络分区与崩溃)、Radioactive(高强度存储破坏)三级。 - 团队文化趣味:Joran 强调构建分布式系统时音乐非常重要,团队必须有一份能激发斗志的专属歌单。 ### [55:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=3300s) 进阶对抗:Red Desert 与 Radioactive 挑战 随后难度提升至第二关“Red Desert”。此关卡模拟了如同 Jepsen 测试般的纯网络故障环境与节点崩溃,场景陷入了多节点角逐领导权的“墨西哥对峙”(Mexican Standoff)。由于主节点(Primary)崩溃下线,副本之间触发确定性的视图变更(View Change)。Joran 指出,与许多依赖随机化来决定 Leader 的协议不同,VSR 具备可预测下一个 Leader 的确定性优势,TigerBeetle 将这一特性充分发挥到了极致。界面上支持像玩《星际争霸》(StarCraft)一样选中单个节点单位查看其详细状态,系统在严苛的网络分区下依然平稳完成重选并推进日志。 在观众的热情呼声中,演示进入了极限挑战关卡“Radioactive”。这一关引入了剧烈的磁盘错误,屏幕上不断降下的电光具象化了模拟中的“宇宙射线”(Cosmic Rays)对磁盘块的破坏,场景中的史莱姆黏液也随着损坏严重程度变换颜色。在经历了数十次连续的存储破坏操作(45、48、49 次连续操作)后,运行在浏览器中的 TigerBeetle 真实集群依然坚挺地存活并保持了一致性。Joran 特别感谢了团队成员的付出,提到来自巴西的 Joey 负责了美术设计,Fabio 则操刀了游戏引擎的开发。 - Red Desert 挑战:纯网络故障注入与节点崩溃,验证 VSR 在网络分区和 Primary 宕机下的确定性视图变更与选主能力。 - Radioactive 挑战:将存储故障与“宇宙射线”随机位翻转可视化,验证集群在底层读写严重损毁时的韧性与自愈。 - 团队贡献致谢:Joey(负责像素艺术与视觉设计)与 Fabio(负责浏览器端游戏引擎构建)。 ### [59:34](https://www.youtube.com/watch?v=w3WYdYyjek4&t=3574s) 会议闭幕致谢与 TigerStyle 的终极心法 在演讲的尾声,Joran 为期两天的 Systems Distributed '23 大会作结,向现场来自开普敦本地的大量技术人员以及从世界各地远道而来的演讲嘉宾表达了由衷的感谢。他动情地回忆起大会的筹备过程:当他宣布要在开普敦举办这场分布式系统峰会时,社区的响应令人惊叹。以嘉宾 Will 为例,Joran 刚发出邀请邮件,连具体的会议日程细节都还没给齐,Will 就已经直接回信说“机票已经买好了”。 Joran 逐一感谢了跨越重洋奔赴现场的演讲嘉宾们——有来自美国和欧洲各地的讲者,有从意大利米兰、匈牙利(Imre)、美国盐湖城(Lindsay)赶来的同行,更有远从印度跋涉而来的 Tiamat 与 Terapai。同时,他向 TigerBeetle 联合创始人 Ful 致以最高赞誉,感谢其在幕后全力以赴确保大会平稳运转,并盛赞所有讲者在不计个人报酬的情况下倾囊相授。在演说结束之际,Joran 总结了 TigerStyle 的精神内核:“So I guess that's the final lesson of Tiger Style. Pay it forward and have fun as you build systems.” 伴随着现场热烈的掌声与片尾曲,首届大会圆满落下帷幕。 - 参会人员与嘉宾来源跨度:南非本地社区、美国、欧洲(米兰、匈牙利等)、印度(Tiamat、Terapai)。 - 幕后致谢:TigerBeetle 联合创始人 Ful 承担了大会绝大部分统筹落地工作。 - 核心精神提炼:“Pay it forward and have fun”(薪火相传,乐在其中),在打造高可靠系统的同时享受工程本身的乐趣。 ## Notable quotes > [20:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=1200s) If it compiles, you've just begun. — Joran Dirk Greef > [43:24](https://www.youtube.com/watch?v=w3WYdYyjek4&t=2604s) If safety is important, you cannot disable checked arithmetic in safe builds. It is madness to do that, right? But yet languages do, and it boggles my mind. — Joran Dirk Greef > [05:00](https://www.youtube.com/watch?v=w3WYdYyjek4&t=300s) So if you find a bug in production, it's disaster because now you've had damage. And to fix that bug is going to take you two or three months extra because you have to plan how is this going to impact all your customer base. — Joran Dirk Greef ## Mentioned Joran Dirk Greef, TigerBeetle, TigerStyle, Gerard Holzmann, James Cowling, Edsger W. Dijkstra, Andrew Kelley, Zig, Rust, Go, Antithesis, PostgreSQL, Jepsen, Viewstamped Replication (VSR), NASA, Google SRE, Barbara Liskov, WebAssembly (WASM), Systems Distributed '23 ## Video description Our final talk from Systems Distributed '23: https://systemsdistributed.com. Join the chat at slack.tigerbeetle.com/invite! [Source](https://www.youtube.com/watch?v=w3WYdYyjek4)

Source quotes

[20:00] Joran Dirk Greef: If it compiles, you've just begun. [43:24] Joran Dirk Greef: If safety is important, you cannot disable checked arithmetic in safe builds. It is madness to do that, right? But yet languages do, and it boggles my mind. [5:00] Joran Dirk Greef: So if you find a bug in production, it's disaster because now you've had damage. And to fix that bug is going to take you two or three months extra because you have to plan how is this going to impact all your customer base.

Source quotes

[15:00] Joran Dirk Greef: You want to try and sketch as many different solutions as you can in the design phase because sketches are cheap, rewrites are expensive. [32:15] Joran Dirk Greef: A system has limits and you may as well make those limits explicit so that your design can handle all the edge cases. [28:13] Joran Dirk Greef: If it compiles, you've just begun. [10:00] Joran Dirk Greef: I think the discipline is to defer coding, to wait, until you have the design roughly right. [20:00] Joran Dirk Greef: The big point is just be explicit upfront about what your system should tolerate.

Original transcript

[0:00] *music* Bye. Thank you. So I want to teach you a magic trick. to think of the most difficult distributed systems problem or advanced software project you can imagine. Uh, Perhaps it's a consensus protocol, a database, or an operating system. These are just my examples. I think you will think of like self-driving cars or some medical device, something like that. And then-- so picture this. And then you build it successfully. in a fraction of the time. So if it took 10 years before to build, then with this trick, you do it in four or five, so you still work hard, but with more velocity. So the result is better, safer, faster, simpler to operate, and you sleep well at night, because by the time you're done, it's the most rock-solid system you've ever built. At least this has been my own experience, being lucky to learn this trick or this style of development from people like Gerard Holtzman and James Cowling, and to put it into practice in the creation of Tiger Beetle less than three years ago. So Tiger Beetle is a distributed financial accounting database. It's dedicated. been designed for mission critical safety and performance. And the way that we developed Tiger Beetle is something that gets our team excited. Because when you see these systems run the way they do, It's hard to describe. It's like magic. So I want to share the style of development with you. It's not one that I see very often. Um, People call it crazy. So I want to I want to make you crazy. And I hope that by the end of our time together, the way that you think about programming will have changed. And you'll see that the systems you never dreamed nor dared. to build before, are well within your reach. Did I say it was a magic trick? Normally you see a magic trick once and then you never get to learn it. So you see Super Mario. But today I want to first teach it to you. and then show it to you. Because with tricks as powerful as these, sometimes seeing is believing. with Super Mario. I think Will's talk yesterday is the most incredible talk I've ever ever seen. So thank you so much Will, like what a brilliant explanation of the magic that you do at Antithesis. So I want to talk today about how to design, build and test distributed systems, give you a demo to show you that the style works, And who knows, we might even get to see a live performance of... The Beatles. So are you ready? Let's talk about style. Tiger style. So TigerStyle is how we design, build, test Tiger Beetle for mission critical safety and performance. I can tell you that we wouldn't be here today at least without TigerStyle. There's many reasons that we're here today. HiTradBoy, ZigShowTime. Amplify, Coil, Adrian, and where is Don? Is Don here? Don, maybe you could stand up. Done. Okay, well, anyway, if you got to meet Don, Don is the original Tiger Beetle with me. We started the project together. He coined the name Tiger Beetle. But there's so many reasons we wouldn't be here today. But Tiger Style is definitely one of them. So there would be no database, no Tiger team. no systems distributed conference, at least not three years into the project. And this is the trick because TigerStyle buys you time. It brings the future forward and it does this through three ideas. So they all work together also. This is the magic thing. It's like three ideas and they interact and combust and they accelerate each other. And the three ideas are these. So Tiger Style sees the whole picture. Tiger style. checks yourself before you wreck yourself. And TigerStyle gives you a time machine. So, [5:00] It gives you a time machine. So does this sound like magic? I'm going to assume that you are already you've already received what I call the blessing of a hard problem. If you have a hard problem, you're blessed, like count yourself lucky. So that's why I have a job. So I'm assuming that you have a hard problem that if solved would be valuable to a lot of people. So this is not about how to find something to work on, but rather once you've found a hard problem, how do you solve it? Where do you start? So I think it's always tempting as you start a project, you know, you survey the landscape of popularity. So we always talk as programmers, oh, that's an appeal to authority. That's a logical fallacy. And yet what do we do? We use Rust because you get a Rust job or it's popular, right? there's other things like NPM or GraphQL, and you pull in these dependencies just because everybody's doing it. and um tiger style messes with all this i can promise you that so because with tiger style you're probably you're not going to decide to use something um merely on the basis that it's popular so you should use rust because rust is amazing with the borrow checker with memory safety that's why you should use rust not not because you see other people using it like decide decide your tools on on quality not quantity But with TigerStyle, it still messes with things because you'll also wait before you code. You're not going to jump into coding. And even then, when you do code, you're going to go slower. So it's going to slow down your coding. You're not going to be able to choose stuff because it's popular. You'll choose stuff sincerely because the BorrowChecker is amazing. And you're going to wait before you even code. So it's going to be just incredibly frustrating. I can promise you that. like an ancient martial artist I don't know if you can guess who this is it is one but like that martial artist your practice restraint and yet Even practicing restraint, you'll move faster, more velocity. So how is this possible? And it's because developing a system is not only about the coding phase, you know, when you cobble stuff together. It's about the design phase before that. and it's the testing during and after, and it's the operator experience in production. the production incidents, which the other team deals with. So in other words, whereas coders, we tend to focus too much on coding, TigerStyle zooms out to see the whole picture. And the whole picture or the cost or time it takes to build a system, the total cost of ownership, looks a little bit like this equation. So the time or the cost is a function of design, coding, testing and production incidents. So if we ship, how good will it be? We can ship quickly, sure, but how good will it be? Will it take down production? At what cost? Will the result achieve our design goals? Will it be valuable or remarkable? You know, sometimes, how often as engineers do you think the technological choices you make, are they going to make the business survive and thrive? Are they going to be remarkable? Will it impact marketing? There's so much as engineers that we've got control over, we don't even realize, you know, you can make really remarkable decisions, you know, like static allocation. design coding and testing it's hard i think to speed up the coding which is what we all try to do so we type faster right you can get a 2x win that's that's not order of magnitude 2x is you know it's it's in in our world it's marginal it's not material so you can speed up coding so much there's only so many hours in the day that you can code sustainably but if we turn to the design phase we have opportunities to speed up because the design informs what we code And also what we don't have to code. So it's great to be lazy. in the design phase do things that you don't have to code. You can design code away And if we The corollary to this is that if we don't spend time on design, we can't speed up the coding phase. So at the same time, if we move too early out of the design phase and into coding testing production, a bad design will become more and more expensive and take longer and longer to fix. So if you find a bug in production, it's disaster because now you've had damage. And to fix that bug is going to take you two or three months extra because you have to plan how is this going to impact all your customer base. You roll it out. It's a disaster. just manifests and it's worse and worse. So, moral of the story is, [10:00] Don't be hasty. An hour or day of design is worth weeks or months in production. Be like the Ents when you go into war. be a force to be reckoned with and wait wait until you are so I love this quote by Dextra how it marries elegance, which is something pretty subjective, like does subjectivity have value to engineers who are so objective? But Dijkstra brings these together. So he marries elegance with execution. And he says the simple and elegant systems tend to be easier and faster to design and get right, more efficient in execution and much more reliable. So I think the discipline is to defer coding, to wait, until you have the design roughly right. You don't have to have it perfectly right, but you want to be on the slopes of the global maxima. If the numbers don't add up. or if your gut feel is bugging you, like for some reason. then just wait. Like Sam and Frodo, you want to be You want to wait until you're on the slopes of Mount Doom, until you're staring up. And you want to find the biggest fiery mountain and then solve for that. So don't, you know, don't. stop your design when you're at the local maxima, find the global maxima, the big fiery Mount Doom, and then go for it. So for Tiger Beetle, we were just lucky with this, right? So I'm sharing our experience. We were just really lucky because the design had to wait. We had a three and a half month period for it to simmer from March to July 2020 before coating started. So, and if it hadn't, many of the key design decisions in Tiger Beetle just wouldn't be there. So, and for many projects, it's easy to skimp on design, yet the design phase is shorter than than the other phases. So in the design phase you can sketch, you can sketch again without writing code You can think through the system in your head, talk it through with your team to flush out showstoppers and you can get a feel for performance so this is cheaper than diving into coding only to back yourself into a corner and then have to redo everything so design it's so nice you just redo redo it's very cheap You can iterate faster in design, and each time you iterate, the result gets like, I don't know. It's like, It's exponentially better. So you can spend twice as long on design to let the design simmer. And over the course of, say, like a two-year project, that extra month is only 4%. when you compare it with the coding and testing phases, yet... that 4% is going to give you like a 10x impact on those phases or 20x or 1000x for Tiger Beaver was literally 1000x And So, and this I think is true, especially with regard to testing. Testing I think this is my opinion, but I think testing is what really dominates the development of complex systems most of all and The reason is that for simple systems people say you know well testing time is is roughly equivalent to the coding time Takes you five hours take five hours to test take one day two days maybe but for complex systems testing can take longer sometimes decades. This is just the history of these, you know, Postgres took 30 years and we're still solving FsyncGate, you know, in 2020. So for example, like a tool like Jepson is terrific at testing distributed databases. But even with Jepson, if you want to find a bug that takes a week to manifest, you have to run Jepson for a week. and then each time you try to reproduce the bug or you just want to confirm that it's now fixed you have again run it for a week this is just how long it takes right this is the standard of current testing. And this is just assuming that the bug can be reproduced. So luckily we know Super Mario, so there's something else you can do. But what if we could use a time machine to speed up time and reproduce bugs perfectly every time, to shrink the testing phase to a fraction of the coding phase. Let me give you an example. So if it took two years to build Tiger Beetle, What if we could test it in a year? instead of four, that would be a silver bullet. Because normally to test something like tiger beetle is 10 years. um at least five years for the consensus storage engine i don't know like five is is on the short side so let's just say at least 10 years so two years to build 10 years to test what if you can test it in a year like that's a 10x advantage um silver bullet right um so we're going to come back to this idea of a time machine to accelerate testing the big idea so far is just that if we see the whole picture We can go slower in some areas. [15:00] to accelerate others and go faster overall. So it's counterintuitive. You go slow to go fast. And I want to share a few tricks with you now that we've learned along the way when it comes to design. And the first is that I think there's more to software than engineering. So if you believe that software is at least engineering and also more than engineering, that there's a precision and an art to software, that there's hard data, cold logic, and also what people bring to the table, human experience, creativity, information, inspiration. You know, my favorite coder is Kevin Flynn, and Kevin Flynn saw a perfection to software, what he called bio-digital jazz, which I just love saying in every talk I give. right and biodiddle jazz is like a tight beat it's like the hard logic cold you know cold data it's a tight beat but it's also a rare groove something organic something human so if software is an art like this or if you want it to be then the first step is to decide what kind of art do you want to make And here there a great analogy from Daniel Lanoir the music producer He has the saying that there are two kinds of musicians. There are sculptors and there are painters. Musicians who sculpt are like Bob Molly recording an album. So they start with something existing, a wall of sound, like a big block of marble, and then they chip away, chip away, chip away, and then they strip it right down to reggae, which is amazing. All the holes in the sound. But a band like the Beatles are painters. So they start with nothing. Maybe a bathtub and some chains, right? They start with nothing, a piece of paper, and they sketch and they draw. I mean, they studied painting, like they studied art. I think Lennon and McCartney, at least. They sketch and they draw, and then they splash color, layer on paint to arrive exactly where they mean to be. A painter is never lost, but arrives exactly where they mean to be. So I love Bob Marley and I love the Beatles But I think that most software these days is sculpted So you take on all these dependencies like big blocks of marble then you hack away and shape them to get where you want However, we didn't build Tiger Beetle like that because we zoomed out and we saw that the dependencies were going to slow down testing. They were going to introduce safety and performance risk. So we realized it was actually just going to be faster to paint Tiger Beetle. and the thing is also you can't sculpt something like tiger beetle because it wouldn't be strawberry fields forever so this is also like a personal thing right and i've just noticed that some coders some coders have it in them to sculpt they are sculptors so you'll engage with someone and they'll always come with the sculpting approach and that's fine that's just their style others prefer to paint. And as a team, we're painters. So we've realized this, right? And that's where we get our velocity from. So now that we're painting systems out of thin air, the next step is to decide on colors. So I want to give you four. And for systems, I think these are all the colors you need right This is it Like just remember this Network disk memory CPU So Jamie was painting for us in terms of disk and memory If you understand how to paint like that network is the same CPU is the same because the two textures that all these paints share is latency and bandwidth. Yeah, so four colors, two textures. Just remember that this is how you paint. And that's it. And then in the design phase, you do back of the envelope calculations. So Jamie did all the work examples for me. It saved me a lot of time on the slides. Thanks, Jeremy. And you can work out all these colors, work out the shape of your system in terms of network, disk, memory, CPU, latency and bandwidth. You can ensure that you don't have latency spikes, right? And you can see that the system is physically possible. So you want to try and sketch as many different solutions as you can in the design phase because sketches are cheap, rewrites are expensive. It's also worth spending some time to find great names for your components, functions and variables. If you take a look at our TigerStyle doc in our repo, you'll find we actually give you tips like how to solve the second. It's not the first hardest problem, Alana. It's the second hardest problem. But we give a whole lot of tips and they're there in the doc. So go check it out. It's even easier. [20:00] impacting the Zig style guide now like people are clamoring can we have snake case please but the goal overall is just to have your nouns and verbs just right because it shows that you understand the system and you have a clear mental model remember Dijkstra you know clear mental model everything just gets so much easier. So as humans, we have a fault model, and we make mistakes. But good names reduce the odds of miscommunication. And at the same time, machines also have fault models. So it's important to define your mechanical fault models at the outset, network fault model, storage fault model, memory fault model, CPU fault model. For some projects, like Jamie mentioned, like Tiger Beetle, I think it's okay, you just say, look, memory fault model But memory allocation failure must be safely handled. You have to handle it. You can't just panic, right, if you're a database. For other projects, at least this is our fault model, for other projects, even ECC memory might not be enough. And then you're also considering CPU faults, like things at massive scale, and you're looking to Byzantine fault tolerance. But the big point is just be explicit upfront about what your system should tolerate. You can shape your design to reduce exposure to faults in the first place. The way you do this is if you have a storage fault model, if you believe that the disk can go slow or bit rot, then you definitely don't want to put... components on the disk right as in your design all the comp as many as you can keep them off the disk that just uh you know stateless is always better than stateful state is very hard state is building a database you know so if you can in your design move things around as much as possible be very careful where your state is and i don't know use a custom tiger beetle state machine or something to reduce surface area. So the more surface area just in general that you commit to across stateless or stateful components, plus, I mean, just the sheer number of components, if you have like 200 components, very difficult for someone to remember them all. If you have three, much easier. So reduce surface area that you commit to, because the more surface area, the more unexpected interactions you get and the more you have to maintain. So for example, sometimes you find systems that open up multiple ports per process, right? We'll have a port for this, port for this, port for this, and it's actually better, I think, just have one port, because it's less surface area. That kind of system, all else equal, is simpler to deploy. you're using less ports. It's also more efficient. So as you design components, try to reduce their surface area to make them as simple as possible, but no simpler. And it's also important to realize that reducing surface area doesn't mean that you're cutting corners. So on this note, we have a zero technical debt policy at Tiger Beetle. I don't know which one of us is Gandalf. It's probably Adrian who wears the Gandalf hat. But we've kept this policy, zero technical debt policy. I mean, sometimes, look, there's a little bit, but we bring it back. But what this means is that we do the best we know how at present. So if we're busy coding and you realize there's a better way. Like, well, drop it. do the better way. We do it the first time because it's important because it's expensive later to come back, you know, to reload stuff into your head. It takes time to get back into the system. And who can be sure that they ever will? You know, are you going to be sure that you will? get given that chance to come back and fix it? Probably not. So do it right the first time. And then knowing that your work is solid, you also have the second order effect of motivating your team and building momentum. And here when it comes to building momentum, one of my favorite tricks is that when you call your team, to chat about design. when you talk Walk, walk and talk. So let me show you how. So there's something about pacing that gets the creative juices flowing, right? So what we do when we have walk and talks, like every two weeks at Tiger Beetle, we literally like pick a design problem. I'm in the boardroom. Fed's also in Cape Town. So Fed walks around the office and then out into the balcony and then we lose signal. And as I said, come back. But I literally paced the boardroom for an hour. And we switch off cameras. We're on Bluetooth headphones. And then we basically just talk through the problem. We take turns. We pitch multiple solutions. We try and find counter examples why they wouldn't work. And the best walk and talk are where you've spent a whole hour. And then one of you says, hang on, we can design the whole [25:00] them away in the first place. So that's amazing. Then that happens because of these walk and talks. So they're very powerful. But stepping back to all these design tricks, they just put you in a good place for coding. And here, now we're coming to coding at last. So TigerStyle also will slow you down. I promise you that. It will slow you down, but in a way that accelerates all the other phases and contributes to a high standard of code quality. So no matter how good your code or your code review is, no matter how much you design or there will always be the bug that slips past that lets you jump through the roof of Super Mario that we'll find. It's the one that got away and we need to do something about that. Um, But, you know, after spending days on code review, weeks on testing, what other defenses are left? And I think here the best defense is to build your system so that you either run correctly that you operate within safe parameters or else you shut down. if you detect that a bug might be present. So you either run safely or not. That's the guarantee that you make to your users. And you can do this with assertions, right? So we learned about assertions from NASA's Power of 10 Rules for Safety Critical Code by Gerard Holtzman. And they're just simple checks. They let you express what you expect of the code as it runs. So they're like little tripwires. I love them because you're laying tripwires everywhere. You think, ah, someone is going to try and do this. I'm going to lay a tripwire there. So that if they reorder this branch, it's going to go boom, you know, and someone's going to find it. So and Jamie, even when he does like he does code, right. And he asks people to do PR like code review. just to check that they find it. Like, this is how far we take it. At least he's threatened to do this, you know? So assertions, there's nothing more fun, like laying tripwires, like the slightest logic bug, then you shut everything down. But what's great, so let me give you another example, right? Inside a function, right at the top, you've got arguments coming in. what you do now is you start adding assertions. For all of those arguments, you just double-check them, that they are as you expect, and you also check how they relate to each other and the state of the program. And then you do the same for the return value. Now your functions are contributing to correctness, which is actually the job of a function. It's not only to be an abstraction. The ultimate purpose of abstractions is for correctness. And this is really how you use functions. Every function should be checking as stuff comes in, as stuff goes out. in surprising environments or interesting data. So your program becomes self-verifying, which is pretty cool. So if you've been following closely, you might have noticed that I've been saying that assertions are about what you expect. and And I've deliberately shown you only one side of the coin. to make the point. And this is what I think separates programmers from hackers. Will is a hacker. And here's the difference, right? As programmers, we don't often think about what we expect. So we're not we're just trying to code. And sometimes we think about what we expect. Not so not completely. So we don't have this. We don't have our mental model fully formed. And it definitely isn't hardened. Right. So we just want to get stuff to work. And we say, look, if it compiles, it's correct. If it compiles like you've just begun to. Like in systems programming, if something compiles, it doesn't matter what language you're using. I'll say it again, if it compiles, you've just begun. Like this is the difference, right, with TigerStyle, the kind of safety that you're going for. Yeah. And this is because hackers think differently. Where programmers operate in the positive space, it compiles, it's what they expect. Hackers operate in the negative space. So they're not going to come through the front door. They're going to attack the compiler and then attack you. So they just think differently. And they're good at coming up with what you don't expect. and deterministically simulated or tested. I don't think any Mario engineer... expected that. But it happened, right? And hackers are good at that. And I mention all this just because I think, like, if you squint, distributed systems as an environment All your hackers. [30:00] The environment is so complex, it's going to attack you. It's going to find the most interesting scenarios in production. I cannot imagine, right, you facing hackers and you need to do something about them. They're going to surprise you in all kinds of ways. But it's important to think about not only what you expect, but to start to think like a hacker, like what don't you expect? What would be really unusual or surprising so that you know where the lines truly are and then you can assert them. And if you assert them wrong, like if your if your understanding is a bit over tight, you'll hit an assertion crash and you can correct your understanding and learn and and and basically move your, you know, figure out the shape of the box. So for example, if you write a while true loop, as part of passing some data, you don't expect it to run for an hour, right? to get stuck in an infinite loop. Have you ever considered that you've written a while tree loop? Have you ever considered how long you expect the loop to run for? Hands up, anybody? Okay, writing a wild true loop? Okay, good. Well done. But I don't, right? I find it very hard to, like, check myself that, oh, I just wrote a wild true loop. This is incredibly dangerous. Like, do I expect this to run for infinity? Could cost a lot of compute, right? to use a finite for loop with a fixed upper bound, then you can detect runaway loops that can deadlock production. Otherwise, you're going to have the system in production and you're going to try and now figure out why is this thing deadlocked. It's going to be a while true loop. If you limit your loops with assertions, you're going to figure that out much quicker. And again, the upper bound here is only half the story. So think like a hacker, you have to assert the lower bound. So you expect that the loop will run some minimum amount of time. That's the hacker thing to do, you know? So minimum bound, maximum bound, how many times you expect it to be run. And this leads into our next point, which is that I think far too many systems these days are designed like that 90s song by Too Unlimited. You know that song? Like, there's no limit, right? Maybe it's just I'm an older generation, but I always think of that song when I see systems with no limits. It's like, it's the 90s, like there's no limit. Like you have to limit, like you have to have limits. Everything has a limit. A system has limits and you may as well make those limits explicit so that your design can handle all the edge cases. Otherwise, you don't really understand your system. It's not even a black box with square sides. It's just a wobbly blob, you know, that you can't get a feel for because there's no limit. I hope this really sticks in your brain. You should go and watch that YouTube video and then. But you like we need to come back to putting limits on our systems like for loops should have limits, right? Resources have limits. What limits do we expect the design to take? You know, yeah so and this this i mean we just get this from again from nasa's power of 10 rules for safety critical code you have to have limits um otherwise things really fail badly so for example in tiger beetle we don't do dynamic memory allocation after startup so we work out all the limits of everything in tiger beetle all the memory we need for different permutations even of our consensus protocol so consensus protocols go through a lot of permutations we've worked that all out so we know like how many messages we need to allocate up front or for our storage engine all the memory we might possibly need for the to do compactions or for all the concurrency, everything is worked out. It actually wasn't that hard. You can take a look at the code. We've got a blog post about it, but it's actually easier than you think to do that. But static memory allocation is just one example of everything having a limit. So that's what I would like us all to take away is that... Even loops have a limit. Everything is limited. Memory, network, disk, CPU has a limit, right? so you can apply this to everything uh concurrency with respect to disk or network um yeah and as we've seen for loops So you see this idea also like in the Google SRE book. They've got this chapter, I think it's chapter 8, handling overload. And it's important that your system knows the limits because if you don't know the limits, you can't handle overload. So if you want like a rock solid production system, it needs to have limits so that it can do back pressure and yeah, that's it. So but finally, the beauty of having limits like this is that [35:00] You end up with something that's lean and mean. I wish you could see, like, you just go, if you could read Tiger Beetle's code, like, to have a feel for it, you know, like, I've seen it, you know, we've, the team, there's so much stuff on the go, but if you can just get a sense of, like, what you can do with this, it's really lean and mean. and that's because you focus so much on the limits of resources that you don't waste any. So you become incredibly efficient, like 10 times more memory efficient than you ever would have been before. You actually use less memory when you do static allocation. You tend not to waste any. So this build process, the coding phase takes a bit longer, but once you have the limits in place, you can then, what can you do with the limits? You can. Check it. Yeah, you can assert the limits, right? Is that what you're saying, Andrea? Oh, yeah, cool. So you can assert and test the limits, and then you can start to gain test velocity over other systems. So you go slow, and then you can go really, really fast. And that where you really win with TigerStyle because assertions are a force multiplier for testing So we now have more than 3 assertions in Tiger Beetle and every time you run Tiger Beetle you know that these 3 assertions are passing which is a nice feeling like I'm you know I'm running the assertions in production so that's the answer to yesterday's question I just want to show you one one of these assertions so caching is the first hardest problem right? Jamie is smiling because he knows what this assertion is. And caching is easy to get wrong. So when we read a block of data from our cache, sometimes we also read the data from disk, and then we just check it. It's the same, that there's no fsync gate, you know, that the cache matches what's on disk. So a few weeks ago, as we were benchmarking, this assertion failed on a rare workload, and we knew something was up. So it took Jamie eight hours to debug, and he wrote up a post on his blog about it. It reads like a Sherlock Holmes novel. And that's because without this assertion, the bug might have taken months or years just to catch with catastrophic consequences. But at least with all these systems the assertion caught it we didn have test coverage but an assertion caught it while benchmarking Still took eight hours to debug but that a lot better than months or years So if assertions are so powerful, why do so few codebases use them? Why do we write functions that never bother to double-check inputs and outputs? And I think that the problem starts at the language level. So, again, I use this quote often, but Barbara Liskopf said that if you want to teach or you want to communicate new ideas to programmers, you have to give them new programming languages to think in. But sometimes our languages take assertions away from us or they default to switching them off in safe bolts, right, in production, which is ironic because that's where bugs have the most power to do damage in production. so we switch, this is what we do, we go into production we switch off all our safety checks and that doesn't make sense so for example, Go is a modern systems language and it's really, Go is a great language for many reasons and we use Go in the workshop at Tiger Beetle for some of our operations stuff and we like Go right but in the FAQ we discover that Go does not have assertions So while they are undeniably convenient Go experience has been that programmers use them as a crutch to avoid thinking about proper error handling and reporting So proper error handling means that servers continue to operate instead of crashing after a non-fatal error. So there's a few problems with this answer, I think. it means well. But the problem is that assertions, when you truly understand them, it's not about convenience. It's correctness, right? It's mission critical. And sure, some programmers may misuse assertions to do error handling instead of just doing error handling. But it doesn't follow that we should therefore be deprived of assertions at a language level. And most of all, the answer it conflates operating errors, which are errors that you can handle, with programming errors or bugs, which are errors you can't. So for example, if your server is low on memory, the thing to do is to not accept more connections. So you're low on memory, you try to malloc, you get an error, don't accept more network connections. You don't crash with a global panic. That is not good to do. [40:00] Because then anyone can DDoS you, right? So, So... I think Go gets this part at least. And obviously assertions shouldn't be used to handle operating errors like this because assertions are there to catch bad code. So in other words, like we've said, programming error where a bug is about to cause unexpected results. But you might wonder if I can detect programming error why can't I handle it? So I'm going to think of all the bugs in advance and handle them, right? The answer is that if you can predict a bug to handle it correctly, why not fix it in the first place? You know, by definition, bugs can't be predicted. There is no proper handling. You're into undefined logic territory. The safe thing to do is shut down safely. You can't continue to operate. And then someone will ask, well, how can you put assertions in a database available. And I think you can because correctness is more important. There's a cost to shutting down, but the cost to not doing so is greater. So, for example, so if you knew, right? You've built this system and you knew. It's about to corrupt tens of thousands of financial transactions every second. Are you really going to say, let's not shut down, we must continue to operate? I don't think so, right? The question is not if you shut down, but look, how quickly can we? Because, you know, if you're going to shut down manually or automatically, that could be the difference between your business staying in business or going out of business. So here assertions are going to automate this nicely for you. As soon as you hit that buggy code, shut down. And don't turn that off in production. And don't have your language turn it off automatically by accident for you in production either. So at the same time, distributed systems, they tend to have fault tolerance baked in. So this means they're actually running several processes of the same thing in a cluster. So they expect that some processes will crash and then they can be restarted. So assertions can actually work really, really well in a mission critical setting all the more. And you don't actually lose high availability. In fact, you get higher availability by crashing. Again, counterintuitive, because the assertion helps the faulty process recover. It gets it out of that bad state. Just crash it, bring it back. When you do, it's so rare, you're probably not going to encounter that bug again. but now you've brought that process back up. But now you're also going to have visibility into the bug to fix it. You're getting rid of all these latent bugs in your system. So while some languages discourage assertions, others, so yeah, all your base are belong to us. Zig is a fantastic language for writing databases because databases are all about working with memory. And I don't know a better language for working with memory with so many sharp tools as Zig. Zig encourages you to do assertions in all the right ways. like addition, subtraction. The ZIG compiler is going to go and lay little tripwires, because Andrew is just like that, right? We're going to put a tripwire there. So if your type overflows while doing addition, you're going to get a nice assertion failure. Yeah, stack trace. So you get checked arithmetic in Zig, and it's enabled by default because it's a good thing, right? So in safe builds, checked arithmetic on by default. That should be the standard for every language. If safety is important, you cannot disable checked arithmetic in safe builds. It is madness to do that, right? But yet languages do, and it boggles my mind. So I hope that all languages move to this place where we have checked arithmetic enabled by default in SafeBoltz. If people want to opt out of that, they can. And like Zig, you can. If you've got data plane code where you've got 100% coverage, sure, you can annotate blocks and say, look, disable assertions. You can still have the assertions there and then selectively disable them, Not automatically opt out, right? But with Zig, you can take assertions even further because Zig lets you do assertions at compile time. And this is not macros. This is like real code. So this is powerful because you can check a program's design integrity before it even executes. So you can crash while compiling. I mean, that's a better crash. Better to crash in CI than to crash in production, right? You can do that with Zig. integrity before it even executes, you know, just while you're compiling. So this is one of the main reasons we picked Zig for Tiger Beetle. Zig's overall approach to correctness, you know, thinking of systems, that really resonated with us. So the Android [45:00] Chiz? You've designed, I think, the most extreme systems language I know. I'm personally convinced that Zig is going to become a tsunami of the next 20-30 years. And if game development is any litmus test, you know, for where the hard systems problems are, I'm really excited about Zig, all the game dev flocking. It's got such an amazing performance culture, and I'm pretty sure Zig is soon where it's going to be. So finally, coming into land, I promised a live performance of the beetles. Before we get there, right, so assertions not only help to make your system safer or to find bugs faster, But as a second order effect, they document your invariance to accelerate your velocity as your team grows. Now in your code, people can see, ah, Andrew was being tricky, he laid tripwise. Why did he lay that trap? Oh, okay, I've learned something I didn't expect. Because now you're showing programmers the positive space, your logic, and you're showing them the negative space, what shouldn't happen. That is incredible. Like that is way more. I mean, comments are important for intention and motivation. But if you writing two sides of the code, I get goose flesh just to think of how powerful that is. Right. You now someone is reading code. They're seeing what should work and they're seeing what shouldn't. Can you just imagine? I mean, most code is not written like that. But that is what teachers do. They want to teach you the right way, and they also want to teach you the wrong way. Then you really understand something. So systems coding that is really understandable is full of assertions, right? Helps accelerate your team. So I've been coding for, not that it means anything, but I've been coding for 28 years. And sadly, very sadly, I only came to appreciate the power of assertions in the last seven. So think of how much time I lost. I think I could be 20 years younger. But thankfully, TigerStyle gives you a time machine. So we designed Tiger Beetle to be able to run in a time machine or simulator that can speed up time. I'm going to show it to you. So this simulator can simulate the network between Tiger Beetle processes or replicas. It can corrupt the disks that they run on. So we simulate the disk also. And this puts Tiger Beetle under like enormous stress because we even corrupt all the disks simultaneously. Like we try to be really tricky. So push the faults to the very theoretical limit. And then we inject all kinds of random network stuff, storage latencies. we maintain strict sterilizability, the highest consistency, and that we have no assertion failures. And then if we do violate correctness or we hit an assertion failure, then we can replay these simulations again and again in like a few seconds to debug any bugs we find. So the velocity you get with this is exhilarating. So you go slow, go a little bit slower, and then it's just incredibly fun. So when we started doing this, I don't know, it feels like Mark 10, right? When we first switched on our simulator, we found and fixed 30 bugs, like crazy distributed bugs, in just three weeks. Every day was just adrenaline. It was the velocity. I've never felt that before. And without the simulator, like these bugs would have taken hundreds of people, years and years of work. You know, the cost is I can't even fathom. So now we've looked at how to do the magic trick. And like I said, I want to show it to you. End off with a quick demo of our simulator. So let's do Thank you. Okay, cool. So this is our simulator code. It's in the repo. It's called simulator.sig. Just Two quick things to note: if you look down where I've highlighted sort of middle of the screen We've got a distributed database type that we constructed compile time using Zig. We're passing in our own business logic, which is the state machine. We're passing in our own message bus, so we could provide any kind of way of processes to talk to each other. We're passing in our own storage backend, so you could swap, make the storage pluggable. We're even passing in our own time source, so we can speed up time. And then Zig gives you this replica, which is a distributed database type. Then we run a few of these replicas all in process in memory, and we simulate. We let them talk to each other and we've got a like a network simulator different latencies. So let's scroll down and let's look over here Um, these Now we create this cluster that's all in process. This demo is not as cool as Super Mario, right? This is just text. But I hope you can get the impact. We have a random seed, from the seed, [50:00] You decide, look, this is what our network is going to look like. It's going to have certain latency distributions or characteristics. It's going to drop packets. It's going to have partitions. It's even going to have asymmetric bandwidth, asymmetric latencies, you know, between two of the same nodes. One node is going to be slower to the other. But then we go further, right? This is very, I don't know of other systems that really do storage fault corruption on the read and write paths. fraction of the time with tiger beetle we just like to corrupt like 30% of the time like everything we just if you're gonna read something 30% of them Corrupt it. Write something. Let's just write it to the wrong place. It's just more fun, right? Then we like to crash Beatles as well. So let's run this. We've got this thing called the VAPA. If you've seen the movie War Games, they have the WAPA, which is a war simulator. This is a view stamped operation replicator. That's why it's called the WAPA. And we're going to run it now. Here we've got a seed. From that seed, we've got three replicas in the cluster. You know, three machines make up a logical Tiger Beetle database. We've got 27 clients that are going to send requests to the database. And we're going to run it. We've got some probabilities of latencies. We're going to replay network messages 34% of the time. We're just going to test this in-potency a lot. Right? Then we're going to do 3% of reads will get corrupt and 3% of writes will get corrupt. We're not really going to crash too much. Now what you see it's running. This is now replica one over there, and it has transitioned from this state to this state, and we've checked that for strict realisability, so it's correct. Given the client requests that were in flight, our simulator can see the whole world, so it knows exactly what is correct and what isn't. and two again, and two, and now zero. So you can see the replicas are catching up, and the cluster is now working together. Now, Replica 2, we've done a leader election, something must have happened to Replica 0. So we've done 2 on the right, that is now the leader, and it's now advancing states. And we run the simulation and And now we do a new simulation. We passed. And this can check. Like, this simulator will tell you, is it a consensus violation, or is it just a liveness issue or deadlock? It can, like, categorize all that for you. And we're running here. Yeah. And that is our simulator. But I want to take things up a level. um... Thank you. So we're not done. And this is thanks to Joey and Fabio, right? This has been our Skunk Works project for a little while. I met Fabio in Milan in April last year. And we've been working on things part time. So what you see here is the same simulator. And we just thought, look, it would be fun because we've got with Tigerstar, we've got all this extra time. Let's let's make a game. Right. So because that's what you do. Right. So I was inspired by Will yesterday and thought, well, we need some cartridges. So we've got three games that I want to play for us. The games are called City Breeze, Red Desert and Radioactive. It's going to run real Tiger Beetle code. This is running in the browser. Thanks to Zig, you can compile stuff to WASM and just run a whole distributed database, just run it in the browser. So that's what you're actually seeing here. And then we're also going to do Mario Brothers simulation tests and just do some storage fault stuff. But City Breeze is cool because everything is perfect. This is how we write our distributed systems. Everything is perfect, right? Because we don't expect anything. work. So here there's no network faults, nothing, like no nothing ever crashes. This is the optimal conditions and we're going to watch view stamp replication across the database with no crashes. So let's see, we need the music to be nice and loud. Thank you. And we're going to see the Beatles descend. There they are. And you'll know like music is very important like if you're building distributed systems. You need to have a playlist called Cyga Tracks or something. It really like motivates the team. And so what you're seeing here is all our like network faults and storage faults and these are all peachy like there's nothing You know, [55:00] Nothing to Yeah, OK, we finished. Yeah. Too easy. Shall we level up, right? Or shall we stop there? Level up, okay, right. Next level. Thank you. Okay, this is Red Desert. Thank you. Amen. This is more difficult. You can hear it in the music. This is like what we call a Mexican standoff. So different beetles are going to decide who gets to be leader and they're both going to vie. So we've got like the primary over there. This beetle is crashed. This one, oh, it's recovering. And we're going to see view changes happening. They're going to do leader election. But our simulator is checking everything, so if the screen crashes, you know there's a bug in Tiger Beetle. If we get to the end, we're good. Yeah, and what's cool is we play like Starcraft and stuff so you can select your units It's a lot of... And this fetal is doing great. Like that's Captain America up there. That's definitely the leader. Let's go this one. Maybe we'll see like a leader change. So far it's just doing normal replication, like preparing and backing up, and then it gets the OK message. - Thank you. you And what's important with this game is that the only faults we're putting in are like what Jepson would do. So we're just messing with the network, that's it. But Tiger Beetle is surviving, right? It's all fine. *Loud music* . It's taking a bit longer because The beetles are coming and going. Amen. Ah, so the primary crashed, right? So now we're going to see a view change. This one thinks it's going to be the new primary. And it is. Now we're onto this one. Now you're starting to see these type of replications deterministic view chains. decide on a leader randomly. VSR is a lot cooler I think because you can predict the next leader so we exploit that entirely. We made it right? So Kyle would love these partitions. Let's stop there because this is sort of, you never test databases beyond this, right? It's getting harder. It's getting harder. Okay, cool. So what should we do next? Radioactive. Radioactive, okay. Cool. So now we're doing disk corruption And the slime does glow. If you really focus on the slime, it will change the color. You're going to really inject a lot of storage spots. You'll hear it in the music. Thank you. So all those zaps are cosmic rays which we just visualized for you. you . So far the Beatles are making it through. No. And We've done like 45 operations, 48. . *music* . *music* 49 And this is like real Tiger Beetle code. We're running a cluster of higher than many in the browser, in a simulator, and we're zapping and they survive. So, it's very special that Joey and Bia all the way from Brazil, he did the art, Fabio did the game engine. Thank you. And then I just want to say thanks. That's the end of the conference. It's been an awesome two days. [60:00] all of you for coming um it's so awesome that we could have such a great local turnout and to the venue it's really beautiful um and to all the speakers what was amazing that you know just telling people we're going to do a conference in cape town everyone's like yeah i'm coming so i i'm picking on will a lot i'll pick on him one more time but i i emailed him to invite him and he replied he said yeah i've booked my flight and i had barely given him the dates or anything the speakers, like really appreciate that you could come out, all of you came from the US or from Europe Um, Milan, Tiamat coming all the way from India, it's just really special. Terapai as well from India, Imre from Hungary, I think Lindsay from Salt Lake City, right? Yeah, thanks so much for coming. And... is really special, we can be systems and distributed. And then also to Ful. So Ful is on the Tiger Beetle team, one of my co-founders. Ful's done like all the effort you know making things run smoothly so a huge huge round of applause And yeah, and to the Tiger Beetle team also, everyone just making things happen. And then, yeah, finally, just again to, you know, the speakers are also like real heroes for us, like all of you, you know. So the fact that you wanted to pay it forward and, you know, even at own expense, come out here and speak, that's paying it forward, right? So I guess that's the final lesson of Tiger Style. Pay it forward and have fun. as you build systems. distributed. Cool. *outro music*