Searchable transcript of The anti-vibe coding philosophy — Awesome (08:49). Search for a phrase, then click its timestamp to jump straight to that moment in the video.
Captions sourced from the original video on YouTube, published by Awesome. The video, its captions and all related intellectual property remain the property of their respective owners; AINotes claims no ownership. Provided for research, accessibility and search — see the Transcript Notice and Copyright Policy.
00:00 For the next 10 minutes or so, let's pretend the year is 2020, the LM craze is not in full swing, and things like software quality and coding standards still matter. Tiger Style's slogan is to do the hard thing today, so tomorrow becomes easy. These days, most of the software world would probably react like >> Ain't nobody got time for that. >> But I have this feeling that we'll all end up paying soon enough for the easy life we are enjoying in the vibe coding, prompt engineering, and agent loop era.
00:26 By the way, you can call me stupid, but I still have no clue what's the deal with this loop agentic engineering, but that's a topic for a different video. Anyway, back to Tiger Style. In this Monday morning review, we look at a coding philosophy focused on safety, performance, and developer experience brought to you by the developers behind the database designed for processing financial transactions at scale.
00:45 I'm mentioning this because TigerBeetle sits in the middle of modern financial systems, where a small mistake can have massive implications with things like duplicated transactions or corrupt balances. When you are working directly with your customers' hard-earned money, the pressure is on, since people tend to be a bit more annoyed if something goes wrong.
01:05 So, Tiger Style focuses on three main values: safety, performance, and developer experience, and this order is actually important. This already makes things slightly unusual because most modern development philosophies start with developer experience. After all, we usually look for the framework that's the most popular and easiest to learn, and that's how we all ended up with React all over the place.
01:24 Don't get me wrong, developer experience matters, but when building serious software, it comes after correctness and performance. Without further ado, let's jump right into the nitty-gritty. One of the core rules is that everything ranging from queues to buffers or basic loops should have an explicit limit because memory usage must be predictable. In practice, we all wrote simple code like this one, which works well if you are building in an ecosystem famous for having major security issues every other week.
01:50 But, if you want to follow best practices, the code should always make the amount of possible work explicit. By the way, don't worry about the programming language in these examples because these rules generally apply if you're aiming to build truly reliable software. So, the program defines the maximum amount of work it can accept and checks that the assumption still holds.
02:10 Also, note that Tiger style also prefers explicit integer widths such as U32 instead of architecture-dependent types such as Usize where possible. For a bit of context, types like Usize exist in languages such as Zig and Rust and their size depends on the target architecture. On a 32-bit system, they are 32-bits wide, while on a 64-bit system, they are usually 64-bits wide.
02:32 Memory allocation follows the same approach. TigerBeetle does not perform general dynamic allocation after initialization, and this is actually kind of fascinating. During startup, the entire system calculates how much memory it needs based on configured limits and allocates it before entering the main event loop. In other words, code processing transactions does not repeatedly allocate and free heap objects.
02:54 Instead, the implementation uses more specialized data structures. This avoids allocator latency and fragmentation during transaction processing, while also reducing the number of lifetime-related states [music] the program has to handle. Memory fragmentation is not something we usually think about in higher-level software, but it becomes important in long-running low-level systems that constantly allocate and free blocks of different sizes.
03:16 Over time, you can end up with plenty of free memory in total, but split across many small regions that are not large enough to satisfy a bigger allocation. This is usually called external fragmentation. You can also get internal fragmentation, where an allocator gives you a block larger than the amount of memory you actually requested because allocations have to follow fixed size classes or alignment requirements.
03:38 Neither problem necessarily crashes the program immediately, but they make memory usage less predictable. So, when you hear that the memory is garbage collected, we usually take it for granted, but under the hood, working with the memory is actually really, really hard. Tiger style also makes heavy use of assertions, and the style guide recommends averaging at least two assertions per function.
03:58 If you are like me and you almost never use assertions in your day-to-day job, just know that they are mechanism used to encode invariants that cannot always be represented by the type system. If these assumptions stop being true, the program fails immediately instead of continuing with invalid state. Of course, this is particularly important in accounting products.
04:19 If an invariant involving balances or transfers has been violated, continuing execution can produce incorrect financial data. Performance rules are similarly concrete, and the style [music] makes extensive use of batching. Work is collected into bounded batches and processed together instead of paying fixed overhead for every individual operation. This affects control flow as well, and the style guide recommends pushing if statements upward and for loops downward.
04:41 In practice, instead of writing code like this, refactor it and extract the iteration inside a specialized function. Now, the branch is evaluated once instead of once per element. Similar rules apply to data layout. Its structures are designed with cache behavior in mind, so a transfer, for example, is deliberately sized around cache line boundaries.
05:00 Accessing memory can cost far more CPU cycles than performing simple arithmetic on data already in cache. And here is your awesome trivia for the day. >> [music] >> A modern core running at around 3 GHz gets through roughly 3 billion cycles a second. Adding two integers that are already sitting in registers costs about one cycle, and on a wide out-of-order core, it can effectively cost less than that because the machine retires several instructions per cycle.
05:27 Now, compare that to the memory hierarchy. Reading from L1 data cache costs somewhere around four or five cycles. L2 is closer to 14, and L3, which is shared between cores, lands somewhere in the 40 to 60 range. So, reading from the main memory costs somewhere between 2 and 400 cycles, depending on the chip and how busy the memory controller is. In other words, in the time it takes to fetch one value from RAM, the core could have executed several hundred arithmetic operations.
05:53 This is why TigerBeetle spends so much effort making sure that the CPU works on compact data that is already available in cache. Another interesting approach is the actual transaction state machine, which is single-threaded. This might sound like a strange choice for a database designed for extremely high throughput, but financial workloads tend to have a lot of contention.
06:12 If thousands of transactions are constantly touching the same popular accounts, those balance updates eventually need to be processed in a defined order. Adding more threads introduces synchronization, locking, and additional cache traffic without necessarily making this part of the system faster. So, TigerBeetle executes committed transactions sequentially on one core.
06:31 But, of course, the operations that can actually run concurrently, such as disk access and replication, are handled separately. For example, before executing a batch of transfers, TigerBeetle can determine which accounts will be needed and prefetch them from storage. Those reads can happen concurrently. Once the required data is available, the actual accounting logic runs synchronously over the batch without performing storage reads inside the hot loop.
06:54 Another performance rule is to avoid unnecessary copying, serialization, and deserialization in the hot path. Tiger style recommends fixed-size structures aligned around the data the CPU actually needs while avoiding repeatedly transforming the same bytes between internal representations. Imagine receiving a 128-byte transfer over the network, deserializing it into one object, copying it into another structure, converting a few fields again for storage, and then copying the whole thing into another buffer.
07:21 Each individual copy might look cheap, but at millions of operations per second, memory bandwidth starts becoming one of your performance limits. Tiger style therefore treats data movement itself as work that needs justification. External interfaces are treated carefully as well. TigerBeetle tries to convert physical, non-deterministic inputs into deterministic internal inputs before they reach important business logic.
07:46 Time is a good example here. The replicated state machine cannot simply call the operating system and ask what time it is because two replicas might receive slightly different answers. Instead, the primary determines the timestamp and includes it in the operation being replicated. Every replica then executes the state transition using the same value.
08:02 And before wrapping things up, let's look at one of the more controversial rules. TigerBeetle has a zero technical technical debt policy. The idea is that when developers discover a known correctness problem, dangerous performance characteristic, or architectural issue, they should fix it before continuing instead of adding it to a backlog with the assumption that somebody will eventually come back to it.
08:24 Obviously, there is some judgment involved here because almost every software project has imperfect code somewhere, but the actual policy is useful to understand because TigerBeetle is trying to avoid knowingly building new functionality on top of problems that are already understood. If you like this video, you should consider joining our community where I'm posting more dedicated weekly content. Please don't forget to smash all the buttons, and see you in the next one.