<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ronit Dahiya</title>
    <description>The latest articles on DEV Community by Ronit Dahiya (@ronitdahiya).</description>
    <link>https://dev.to/ronitdahiya</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3087860%2F84e25ece-9857-41fb-a772-2dfb61d9ace5.jpg</url>
      <title>DEV Community: Ronit Dahiya</title>
      <link>https://dev.to/ronitdahiya</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ronitdahiya"/>
    <language>en</language>
    <item>
      <title>Stop drawing system design diagrams. Start simulating them.</title>
      <dc:creator>Ronit Dahiya</dc:creator>
      <pubDate>Thu, 23 Apr 2026 20:04:18 +0000</pubDate>
      <link>https://dev.to/ronitdahiya/stop-drawing-system-design-diagrams-start-simulating-them-3m19</link>
      <guid>https://dev.to/ronitdahiya/stop-drawing-system-design-diagrams-start-simulating-them-3m19</guid>
      <description>&lt;p&gt;Your diagram never fails.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8u65eccukl0vrxohmo1v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8u65eccukl0vrxohmo1v.png" alt="Shows System Design Simulator" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's the problem.&lt;/p&gt;

&lt;p&gt;You draw a load balancer, three app servers, Redis cache, Postgres. Arrows everywhere. Looks great. Interviewer nods. You nod. Everyone nods. And then you get the follow-up question: "What happens at 50,000 requests per second?"&lt;/p&gt;

&lt;p&gt;And you hand-wave. "Well, the cache handles most of it, so the database is mostly fine..."&lt;/p&gt;

&lt;p&gt;Mostly fine. The two most dangerous words in system design.&lt;/p&gt;




&lt;h2&gt;
  
  
  The lie we tell in interviews
&lt;/h2&gt;

&lt;p&gt;I've sat through a lot of system design prep. The advice is always the same: draw the components, explain the tradeoffs, mention CAP theorem, say "it depends" a lot.&lt;/p&gt;

&lt;p&gt;Here's what nobody tells you: &lt;strong&gt;a diagram is a snapshot of your best intentions, not a model of your system's behavior.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your diagram shows what components exist. It says nothing about what happens when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your cache cold-starts after a deploy&lt;/li&gt;
&lt;li&gt;Two pods restart simultaneously and 40,000 requests hit your database directly&lt;/li&gt;
&lt;li&gt;Your message queue backs up because a downstream service is slow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The diagram sits there looking perfect while your system falls apart in ways you didn't anticipate.&lt;/p&gt;

&lt;p&gt;I got tired of hand-waving through these scenarios. So I built something that actually runs them.&lt;/p&gt;




&lt;h2&gt;
  
  
  What simulation adds that drawing doesn't
&lt;/h2&gt;

&lt;p&gt;I built &lt;a href="https://syssimulator.com" rel="noopener noreferrer"&gt;SysSimulator&lt;/a&gt; — a system design simulator that runs entirely in your browser. You drag components onto a canvas, configure them (cache hit rate, database connection pool size, replica count), set your RPS, and hit run. It uses a discrete-event simulation engine compiled from Rust to WebAssembly, so it's running real math, not vibes.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(The engineering behind the Rust/WASM part is in &lt;a href="https://dev.to/ronitdahiya/i-compiled-rust-to-webassembly-to-build-a-system-design-simulator-that-runs-entirely-in-your-3j1n"&gt;my previous article&lt;/a&gt; if that's your thing. This one is about what you actually learn from running it.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's the thing about running a simulation: &lt;strong&gt;it tells you things your diagram can't.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The cache stampede I didn't expect
&lt;/h2&gt;

&lt;p&gt;Let me give you a concrete example. Classic interview scenario: high-read system with a Redis cache in front of Postgres.&lt;/p&gt;

&lt;p&gt;I drew this a hundred times. Cache handles 90% of reads, database handles 10%, everything is fine.&lt;/p&gt;

&lt;p&gt;Then I simulated it.&lt;/p&gt;

&lt;p&gt;I set up the blueprint: load balancer → app servers → Redis → Postgres. Cache hit rate: 90%. Starting load: 1,000 RPS. Then I hit the "cache expiry" chaos scenario — simulates what happens when your cache TTLs expire simultaneously, which happens after a cold start or a cache flush.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What my diagram predicted:&lt;/strong&gt; cache handles 90% of reads, brief spike on the database, recovers quickly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What actually happened:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;p99 latency: 48ms → &lt;strong&gt;2,400ms&lt;/strong&gt; in 11 seconds&lt;/li&gt;
&lt;li&gt;Database error rate: &lt;strong&gt;34%&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Queue depth: backed up to 8,000 pending requests&lt;/li&gt;
&lt;li&gt;Recovery time after cache refills: 4 minutes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fickky8ugon1ac828bsrj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fickky8ugon1ac828bsrj.png" alt="Simulation Metrics" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The diagram said "brief spike." The simulation said "your database is on fire and users are seeing 2.4-second loads for 4 minutes."&lt;/p&gt;

&lt;p&gt;That's a cache stampede. It's a known failure mode — there's even a Wikipedia article about it — and my beautiful diagram completely missed it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this changes how you talk in interviews
&lt;/h2&gt;

&lt;p&gt;Here's the interview prep angle that I think gets overlooked.&lt;/p&gt;

&lt;p&gt;When you simulate a scenario, you get &lt;strong&gt;specific numbers&lt;/strong&gt;. And specific numbers change how you talk.&lt;/p&gt;

&lt;p&gt;Before simulation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The cache handles most of the load, so the database should be fine."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After simulating the cache stampede:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"At 10K RPS with a 90% cache hit rate, the database normally handles around 1,000 QPS — well within its connection pool limit. But if the cache expires simultaneously, we lose that 90% hit rate for about 60-90 seconds. That sends the full 10K RPS to the database, which saturates the connection pool. p99 spikes from ~50ms to over 2 seconds, error rates hit 30%+. The fix is probabilistic cache expiry — instead of all keys expiring at the same TTL, you add jitter. Each key expires at &lt;code&gt;TTL + random(0, TTL*0.1)&lt;/code&gt;, so the stampede becomes a trickle."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Same underlying knowledge. Completely different delivery. The second version sounds like someone who has actually seen this happen.&lt;/p&gt;

&lt;p&gt;You haven't seen it happen — but you've run the simulation, and that's close enough to talk about it with conviction.&lt;/p&gt;




&lt;h2&gt;
  
  
  The narration framework that actually works
&lt;/h2&gt;

&lt;p&gt;For system design interviews, I use a three-part structure for any scenario:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The steady state&lt;/strong&gt; — what happens at normal load&lt;br&gt;&lt;br&gt;
"At 10K RPS with warm cache, p99 is around 45ms. Database handles 800 QPS, well within its 2,000 connection limit."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The failure mode&lt;/strong&gt; — what breaks it and why&lt;br&gt;&lt;br&gt;
"If we lose the cache — cold start, flush, or a stampede — the full load hits the database directly. Connection pool saturates in about 8 seconds."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The fix&lt;/strong&gt; — specific, not vague&lt;br&gt;&lt;br&gt;
"Cache-aside with jitter on TTL. Or serve stale-while-revalidate with a background refresh. Either approach limits the blast radius of cache expiry."&lt;/p&gt;

&lt;p&gt;The simulation gives you the numbers for part 1 and 2. Part 3 is where you show you understand the tradeoffs.&lt;/p&gt;

&lt;p&gt;I wrote up a &lt;a href="https://syssimulator.com/interview-prep" rel="noopener noreferrer"&gt;longer version of this framework&lt;/a&gt; with the exact narration for the cache stampede scenario — word-for-word, with the numbers. It's what I'd say if I was on the spot in an interview right now.&lt;/p&gt;




&lt;h2&gt;
  
  
  Running it yourself
&lt;/h2&gt;

&lt;p&gt;If you want to try the cache stampede scenario specifically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://syssimulator.com" rel="noopener noreferrer"&gt;SysSimulator&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Load the "Three-Tier Web App" blueprint (it's in the blueprints panel)&lt;/li&gt;
&lt;li&gt;Set RPS to 10,000&lt;/li&gt;
&lt;li&gt;Run the simulation — watch p99 and error rate in the live metrics bar&lt;/li&gt;
&lt;li&gt;Hit the "Cache Stampede" chaos scenario&lt;/li&gt;
&lt;li&gt;Watch what happens to p99&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It takes about 3 minutes. At the end you'll have specific numbers you can use in any interview that involves a cache.&lt;/p&gt;

&lt;p&gt;The tool is free, no login, runs entirely in your browser. The simulation runs in WebAssembly so it's fast — 100K RPS simulations run ahead of wall-clock time.&lt;/p&gt;




&lt;h2&gt;
  
  
  What to do with the other blueprints
&lt;/h2&gt;

&lt;p&gt;The stampede is one scenario. There are 57 blueprints total — CQRS, event sourcing, Saga pattern, rate limiting architectures, MCP agent systems — and 28 chaos scenarios you can inject on any of them.&lt;/p&gt;

&lt;p&gt;The ones that surprise people most in practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Read replica lag&lt;/strong&gt; — your replica is "caught up" until it isn't, and now your read-after-write consistency assumption is broken&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thundering herd on load balancer restart&lt;/strong&gt; — all connections re-establishing simultaneously&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queue backpressure&lt;/strong&gt; — your Kafka consumer is 800ms behind, your producer doesn't slow down, your broker fills up&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these look dangerous in a diagram. All of them have produced real incidents. Running the simulation doesn't replace production experience — but it's a lot faster and cheaper than getting paged at 2am.&lt;/p&gt;




&lt;p&gt;I spent a lot of time drawing boxes in Excalidraw and not enough time asking "what actually breaks here." This is me trying to fix that.&lt;/p&gt;

&lt;p&gt;If you find the cache stampede numbers useful for prep, or have a specific scenario you want to model, drop it in the comments. Happy to work through it.&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>career</category>
      <category>architecture</category>
      <category>interview</category>
    </item>
    <item>
      <title>I compiled Rust to WebAssembly to build a system design simulator that runs entirely in your browser!</title>
      <dc:creator>Ronit Dahiya</dc:creator>
      <pubDate>Mon, 20 Apr 2026 10:23:38 +0000</pubDate>
      <link>https://dev.to/ronitdahiya/i-compiled-rust-to-webassembly-to-build-a-system-design-simulator-that-runs-entirely-in-your-3j1n</link>
      <guid>https://dev.to/ronitdahiya/i-compiled-rust-to-webassembly-to-build-a-system-design-simulator-that-runs-entirely-in-your-3j1n</guid>
      <description>&lt;p&gt;Static diagrams don't fail. Systems do.&lt;/p&gt;

&lt;p&gt;That was the problem I kept running into when practicing system design. I'd draw boxes and arrows, convince myself the architecture was solid, and then an interviewer would ask "what happens to your read traffic if the cache goes down?" - and I'd be narrating from memory, not from evidence.&lt;/p&gt;

&lt;p&gt;I wanted to &lt;em&gt;watch&lt;/em&gt; my architecture break. So I built &lt;a href="https://syssimulator.com" rel="noopener noreferrer"&gt;SysSimulator&lt;/a&gt; - a free browser-based tool that lets you simulate real traffic, inject chaos scenarios, and watch cascade failures in real time. No install. No signup. No backend required.&lt;/p&gt;

&lt;p&gt;The interesting engineering decision was the foundation: the simulation engine is written in &lt;strong&gt;Rust&lt;/strong&gt;, compiled to &lt;strong&gt;WebAssembly&lt;/strong&gt;, and runs entirely in your browser.&lt;/p&gt;

&lt;p&gt;Here's why, and what I learned.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why not just use JavaScript?
&lt;/h2&gt;

&lt;p&gt;The obvious choice for a browser-based simulator is JavaScript. It's already in the browser. You don't need a compilation step. Every tutorial on "build a simulation in the browser" uses it.&lt;/p&gt;

&lt;p&gt;But simulation engines have a specific performance profile that JavaScript handles badly.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;discrete-event simulation (DES)&lt;/strong&gt; processes thousands of events per second - request arrivals, processing completions, timeout triggers, state transitions. Each event modifies shared state (component queues, error counts, latency distributions) and may produce new events. At 100,000 RPS with 10 components, you're processing hundreds of thousands of state mutations per second.&lt;/p&gt;

&lt;p&gt;JavaScript's garbage collector will pause the world mid-simulation. At high event rates, those pauses become visible - the particle animation stutters, the metrics bar freezes, the simulation loses time fidelity. It's not fatal for a toy, but it breaks the sense of "real" that makes the tool actually useful for building intuition.&lt;/p&gt;

&lt;p&gt;Rust gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic memory management&lt;/strong&gt; - no GC pauses, no stop-the-world&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Predictable performance&lt;/strong&gt; - the simulation advances at wall-clock speed without hitches&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-cost abstractions&lt;/strong&gt; - rich type system and pattern matching with no runtime overhead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct WASM compilation&lt;/strong&gt; via &lt;code&gt;wasm-pack&lt;/code&gt; with minimal boilerplate
The tradeoff is compile time and complexity. It's worth it.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How the DES engine works
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzrp1owjwh47pk8jibl8z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzrp1owjwh47pk8jibl8z.png" alt="Diagram showing DES event loop — Event Queue → Process Event → Generate New Events → Update State → Back to Event Queue." width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A discrete-event simulation has three core concepts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Events&lt;/strong&gt; - things that happen at a specific simulated time. In SysSimulator, events are things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RequestArrived { component_id, timestamp, request_id }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ProcessingComplete { component_id, timestamp, latency_ms }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ChaosInjected { scenario, target_component, severity }&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. The event queue&lt;/strong&gt; - a priority queue ordered by timestamp. The engine always processes the earliest event first. This is what makes DES "discrete" - time jumps forward in steps, not continuously.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. State&lt;/strong&gt; - the current condition of every component. Queue depth, active connections, error rates, latency distributions. Each event reads and writes state.&lt;/p&gt;

&lt;p&gt;The core loop in Rust is approximately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;SimulationResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.event_queue&lt;/span&gt;&lt;span class="nf"&gt;.pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="py"&gt;.timestamp&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.clock&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.tick_duration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.clock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="py"&gt;.timestamp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;new_events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.process_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;new_events&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.event_queue&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.update_metrics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.collect_metrics&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The real complexity is in &lt;code&gt;process_event&lt;/code&gt; - each component type (load balancer, cache, database, message queue) has its own behaviour model. A cache hit generates a fast response event. A cache miss cascades to a database read. A database under memory pressure starts dropping connections. The interactions are what make simulation genuinely useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  Compiling Rust to WASM with wasm-pack
&lt;/h2&gt;

&lt;p&gt;The compilation pipeline is simpler than I expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cargo.toml:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[lib]&lt;/span&gt;
&lt;span class="py"&gt;crate-type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"cdylib"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;wasm-bindgen&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.2"&lt;/span&gt;
&lt;span class="py"&gt;js-sys&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.3"&lt;/span&gt;
&lt;span class="py"&gt;serde&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"derive"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;serde-wasm-bindgen&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.6"&lt;/span&gt;

&lt;span class="nn"&gt;[profile.release]&lt;/span&gt;
&lt;span class="py"&gt;opt-level&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"s"&lt;/span&gt;  &lt;span class="c"&gt;# optimise for size, not speed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;opt-level = "s"&lt;/code&gt; is important - WASM bundles transferred over the network should be small. Size optimisation also tends to reduce instruction count, which helps in the WASM runtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exposing functions to JavaScript:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;wasm_bindgen&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[wasm_bindgen]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;SimEngine&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SimulationState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;event_queue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BinaryHeap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SimEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;clock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[wasm_bindgen]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;SimEngine&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[wasm_bindgen(constructor)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topology&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;JsValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;SimEngine&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Topology&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;serde_wasm_bindgen&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topology&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nn"&gt;SimEngine&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_topology&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tick_ms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;JsValue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.advance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tick_ms&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nn"&gt;serde_wasm_bindgen&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;to_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;inject_chaos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;JsValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;chaos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ChaosScenario&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;serde_wasm_bindgen&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.apply_chaos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chaos&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Build command:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wasm-pack build &lt;span class="nt"&gt;--target&lt;/span&gt; bundler &lt;span class="nt"&gt;--release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This produces a &lt;code&gt;pkg/&lt;/code&gt; directory with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;syssimulator_bg.wasm&lt;/code&gt; - the compiled WASM binary&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;syssimulator.js&lt;/code&gt; - the JS glue code generated by wasm-bindgen&lt;/li&gt;
&lt;li&gt;TypeScript type definitions
The generated JS handles the memory bridge between JavaScript and WASM automatically. You call Rust functions like they're regular JS functions. The &lt;code&gt;serde-wasm-bindgen&lt;/code&gt; crate handles serialisation of complex types (the topology JSON, metrics output) across the boundary.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The WASM loading strategy
&lt;/h2&gt;

&lt;p&gt;First load time is the main UX risk with WASM. The binary needs to be fetched, compiled, and instantiated before the simulation can run. On a slow connection this can be several seconds.&lt;/p&gt;

&lt;p&gt;My approach:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Async loading with a visible loading state.&lt;/strong&gt; The UI renders immediately from static HTML. The simulation controls are shown but disabled. A loading indicator shows "Initialising simulation engine..." so users know what's happening.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Streaming compilation.&lt;/strong&gt; Modern browsers can compile WASM while it's still downloading via &lt;code&gt;WebAssembly.instantiateStreaming&lt;/code&gt;. This is enabled automatically when you serve WASM with the correct &lt;code&gt;Content-Type: application/wasm&lt;/code&gt; header. On Vercel, this is handled automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Persistent caching.&lt;/strong&gt; The WASM binary is served with a content-hash filename and long &lt;code&gt;Cache-Control&lt;/code&gt; headers. After the first visit, subsequent loads are instant - the binary comes from the browser cache.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The generated wasm-bindgen glue handles this, but conceptually:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;WebAssembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instantiateStreaming&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/pkg/syssimulator_bg.wasm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;importObject&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Modelling the 18 component types
&lt;/h2&gt;

&lt;p&gt;Every component in the simulator has a behaviour model that determines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Processing latency&lt;/strong&gt; - a latency distribution (P50, P95, P99), derived from real-world measurements for that component type&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency limits&lt;/strong&gt; - max simultaneous requests before queuing begins&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure modes&lt;/strong&gt; - what happens under chaos injection (node crash, memory pressure, etc.)
Here are a few interesting ones:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cache (Redis model)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;process_cache_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ProcessResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;hit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.rng.gen&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;f64&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.hit_rate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;hit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Cache hit: fast response, no downstream call&lt;/span&gt;
        &lt;span class="nn"&gt;ProcessResult&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.hit_latency_dist&lt;/span&gt;&lt;span class="nf"&gt;.sample&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Cache miss: forward to origin with cache miss overhead&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;miss_latency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.miss_overhead_dist&lt;/span&gt;&lt;span class="nf"&gt;.sample&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nn"&gt;ProcessResult&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;forward_to_origin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;miss_latency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under a &lt;strong&gt;cache stampede&lt;/strong&gt; chaos scenario, the hit rate drops to near zero and hundreds of requests simultaneously hit the origin - which is where the cascade failure becomes visible in the simulation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Load balancer (round-robin model)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;route_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ComponentId&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Round-robin with health check&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;healthy_backends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.backends&lt;/span&gt;
        &lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.filter&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="nf"&gt;.is_healthy&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;healthy_backends&lt;/span&gt;&lt;span class="nf"&gt;.is_empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// All backends unhealthy - request fails&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.counter&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;healthy_backends&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.counter&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;healthy_backends&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="py"&gt;.id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you inject a &lt;strong&gt;node failure&lt;/strong&gt; on one of the app servers, the load balancer's health check detects it and routes around it - but if enough backends fail, capacity drops and latency rises. This is the exact behaviour pattern that shows up in production incidents.&lt;/p&gt;




&lt;h2&gt;
  
  
  The chaos engine - 28 scenarios
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/65EMlDiPLdw"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The chaos system is separate from the simulation engine. Each scenario is a function that modifies component state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;apply_chaos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ChaosScenario&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="py"&gt;.kind&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;ChaosKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;NetworkPartition&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Drop all requests between two components&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.add_connection_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="py"&gt;.source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="py"&gt;.target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nn"&gt;ConnectionFilter&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;DropAll&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nn"&gt;ChaosKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;LatencyInjection&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;p50_ms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p99_ms&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Add artificial latency distribution to a component&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.components&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="py"&gt;.target&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="nf"&gt;.add_latency_overhead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;LatencyDist&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p50_ms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p99_ms&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nn"&gt;ChaosKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CacheStampede&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Force cache hit rate to near zero&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nn"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ref&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.components&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="py"&gt;.target&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="nf"&gt;.override_hit_rate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.02&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nn"&gt;ChaosKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;NodeFailure&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Take component offline - load balancers detect and route around&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.components&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="py"&gt;.target&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.set_health&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ComponentHealth&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Down&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="c1"&gt;// ... 24 more scenarios&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting design decision was making chaos &lt;strong&gt;composable&lt;/strong&gt;. You can inject a network partition AND a memory pressure event simultaneously and watch the compounding failure. In production, incidents are rarely single-cause - this teaches engineers to think in terms of failure combinations.&lt;/p&gt;




&lt;h2&gt;
  
  
  AWS cost estimation
&lt;/h2&gt;

&lt;p&gt;This was the feature I was most uncertain about including, and it turned out to be one of the most useful.&lt;/p&gt;

&lt;p&gt;Every component maps to an AWS service and a pricing model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;estimate_monthly_cost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;topology&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Topology&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CostBreakdown&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;compute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;networking&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;topology&lt;/span&gt;&lt;span class="py"&gt;.components&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="py"&gt;.kind&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;ComponentKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;WebServer&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// EC2 t3.medium equivalent based on configured throughput&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;instance_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rps&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="py"&gt;.throughput_limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.ceil&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="n"&gt;compute&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;instance_count&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;EC2_T3_MEDIUM_HOURLY&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;730.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="nn"&gt;ComponentKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Serverless&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Lambda pricing: per-request + duration&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;monthly_requests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rps&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;86400.0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;30.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;requests&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;monthly_requests&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;LAMBDA_PER_REQUEST&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;requests&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;monthly_requests&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="py"&gt;.avg_duration_ms&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;1000.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
                    &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;LAMBDA_PER_GB_SECOND&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="py"&gt;.memory_gb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="nn"&gt;ComponentKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// RDS db.t3.medium for the configured storage tier&lt;/span&gt;
                &lt;span class="n"&gt;compute&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;RDS_T3_MEDIUM_HOURLY&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;730.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;storage&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="py"&gt;.storage_gb&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;RDS_STORAGE_PER_GB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="c1"&gt;// ... other component types&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;CostBreakdown&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;networking&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The numbers are rough-order estimates, not exact billing. But "adding 3 more app servers costs approximately $280/month" is the right answer to "why not just scale horizontally indefinitely?" - which is exactly the kind of cost-awareness question that separates senior engineers from mid-level in system design interviews.&lt;/p&gt;




&lt;h2&gt;
  
  
  What surprised me about WASM in production
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The good:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance exceeded expectations.&lt;/strong&gt; At 100,000 simulated RPS with 10+ components, the engine advances simulation time faster than wall clock time - there's headroom to spare.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debugging is better than expected.&lt;/strong&gt; &lt;code&gt;wasm-pack test --chrome&lt;/code&gt; runs your Rust unit tests in an actual browser. Source maps work reasonably well with the right setup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The memory model forced better design.&lt;/strong&gt; Rust's ownership rules pushed me toward an architecture where simulation state is clearly separated from UI state. The resulting code is more correct.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The hard parts:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Serialisation overhead is real.&lt;/strong&gt; Every call across the JS/WASM boundary that involves complex types goes through serialisation. Calling &lt;code&gt;step()&lt;/code&gt; 60 times per second is fine. Passing large topology objects on every frame would not be.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error handling across the boundary is awkward.&lt;/strong&gt; Rust's &lt;code&gt;Result&amp;lt;T, E&amp;gt;&lt;/code&gt; doesn't cross the boundary cleanly. I ended up encoding errors as optional fields in the return value rather than using WASM exceptions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bundle size management is ongoing.&lt;/strong&gt; The WASM binary is currently ~280KB gzipped. Acceptable, but I'm tracking it.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Results — what the simulator shows that a whiteboard can't
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2y30or5iq3ab9nr7j1x2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2y30or5iq3ab9nr7j1x2.png" alt="Metrics Showing Chaos Simulation" width="800" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you inject a &lt;strong&gt;cache stampede&lt;/strong&gt; on a 10,000 RPS e-commerce architecture, you see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cache hit rate drops from 98% → 2%&lt;/li&gt;
&lt;li&gt;Database connections saturate within 400ms&lt;/li&gt;
&lt;li&gt;App server queue depth climbs until requests start timing out&lt;/li&gt;
&lt;li&gt;Error rate spikes from 0.1% → 34%&lt;/li&gt;
&lt;li&gt;P99 latency goes from 48ms → 2,400ms
That sequence - and the ability to narrate exactly what happened and why - is what interviewers at FAANG are evaluating when they ask "what happens to your read traffic if the cache goes down?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A static diagram cannot show you that. A simulator built on a proper DES engine can.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://syssimulator.com" rel="noopener noreferrer"&gt;SysSimulator&lt;/a&gt;&lt;/strong&gt; is free, runs in your browser, no account required.&lt;/p&gt;

&lt;p&gt;57 architecture blueprints (e-commerce, chat, payment systems, Kafka pipelines, MCP AI agents), 28 chaos scenarios, real-time AWS cost estimation.&lt;/p&gt;

&lt;p&gt;The source of the WASM simulation engine is something I'm considering open-sourcing - leave a comment if that's interesting to you.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What questions do you have about the Rust/WASM approach? Specifically curious if others have tackled the serialisation overhead problem differently - would love to compare notes.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by Ronit Dahiya. &lt;a href="https://www.linkedin.com/in/ronit-dahiya/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://github.com/IllusionistBoi" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>systemdesign</category>
      <category>showdev</category>
      <category>webassembly</category>
    </item>
  </channel>
</rss>
