<?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.us-east-2.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>28 chaos engineering scenarios mapped to system design interview questions</title>
      <dc:creator>Ronit Dahiya</dc:creator>
      <pubDate>Sat, 06 Jun 2026 16:13:44 +0000</pubDate>
      <link>https://dev.to/ronitdahiya/28-chaos-engineering-scenarios-mapped-to-system-design-interview-questions-4kkh</link>
      <guid>https://dev.to/ronitdahiya/28-chaos-engineering-scenarios-mapped-to-system-design-interview-questions-4kkh</guid>
      <description>&lt;p&gt;"Your cache just died at 30,000 RPS. Walk me through what happens."&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%2Fgjos2ifbyzgsp6o0izeg.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%2Fgjos2ifbyzgsp6o0izeg.png" alt="Twitter System Design SysSimulator" width="799" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most candidates pause, then describe it in the abstract. The cache goes down, requests hit the database, the database gets overwhelmed. Technically correct. Completely unconvincing.&lt;/p&gt;

&lt;p&gt;The candidate who has actually watched a cache stampede answers differently:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"p99 goes from 48ms to around 2,400ms in under a second. Connection pool exhaustion on the database is what causes it, not the database being slow, but the number of concurrent connections queued waiting for a slot. At 30K RPS with a 0% cache hit rate and a database connection pool sized for 10% of that traffic, the pool saturates in roughly 400 milliseconds. Error rate climbs from 0.1% to 34% before any circuit breaker fires."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That answer comes from having seen it happen, not from having read about it. Chaos engineering is how you see it happen before an interview, or before production does it for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why system design interviews now test failure reasoning
&lt;/h2&gt;

&lt;p&gt;The shift is real and it has happened over the last two to three years. Drawing the architecture is the entry bar, not the differentiator. Interviewers at companies running large distributed systems are explicitly looking for failure reasoning: what your design does when a component behaves badly, how failure propagates, and what you designed to contain it.&lt;/p&gt;

&lt;p&gt;The question "what happens when your cache fails?" is not a curveball. It is a standard follow-up to any design that includes a cache.&lt;/p&gt;

&lt;p&gt;So is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"What happens when your primary database goes down?"&lt;/li&gt;
&lt;li&gt;"What does your system do when the payment processor is slow but not timing out?"&lt;/li&gt;
&lt;li&gt;"What happens if all your clients reconnect at the same moment after a 30-second outage?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of those maps to a named chaos engineering scenario. Knowing the scenario means knowing what metric changes first, what cascade follows, and what design decision prevents or contains it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four categories, eight scenarios, eight interview questions
&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%2Fpdkng3m9nludjxlaseel.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%2Fpdkng3m9nludjxlaseel.png" alt="Latency Metrics" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Network chaos
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Latency injection
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What the interviewer is testing with:&lt;/strong&gt; "Your third-party payment processor starts responding slowly. Your checkout service calls it synchronously. Walk me through the impact on your cart API."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What latency injection shows:&lt;/strong&gt; In a synchronous call chain, injected latency multiplies. A 200ms injection on one downstream service adds 200ms to every request that touches it. If your checkout service makes five synchronous calls and one of them is slow, end-to-end p99 climbs by the full injection amount, not a fraction of it.&lt;/p&gt;

&lt;p&gt;The design decision being tested is whether you have identified which dependencies should be async and which genuinely need to be synchronous.&lt;/p&gt;

&lt;h3&gt;
  
  
  Network partition
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What the interviewer is testing with:&lt;/strong&gt; "Your cache and your application servers can't communicate. What does your system do?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What network partition shows:&lt;/strong&gt; Whichever side of the partition keeps running will exhaust its connection pool trying to reach the other side. Without circuit breakers, callers queue requests that will never complete.&lt;/p&gt;

&lt;p&gt;With partition, you see the CAP theorem stop being theoretical. You are making a real choice between consistency and availability, and your design either has an explicit answer or it does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Infrastructure chaos
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Node failure
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What the interviewer is testing with:&lt;/strong&gt; "One of your three application servers goes down. What happens to traffic?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What node failure shows:&lt;/strong&gt; Single points of failure that were not obvious on the diagram become obvious when you kill one component and watch traffic stack on the survivors. If your load balancer is not health-checking aggressively enough, it keeps routing to the dead node. If your remaining nodes were sized assuming N capacity rather than N-1, they saturate immediately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache stampede
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What the interviewer is testing with:&lt;/strong&gt; "You do a rolling deploy and your cache gets cleared. It's 2pm on a Tuesday. Walk me through the next 60 seconds."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What cache stampede shows:&lt;/strong&gt; Forcing the cache hit rate to near zero at meaningful RPS reveals whether your origin can handle the full load. Usually it cannot, because the origin was sized assuming the cache absorbs 80 to 95% of reads.&lt;/p&gt;

&lt;p&gt;The database connection pool saturates, queue depth climbs, and p99 spikes.&lt;/p&gt;

&lt;p&gt;The design decisions it tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Probabilistic early expiration, so the cache never goes fully cold.&lt;/li&gt;
&lt;li&gt;Request coalescing, so a thousand simultaneous cache misses for the same key only produce one origin request.&lt;/li&gt;
&lt;li&gt;Write-through caching, so the cache is never empty after a deploy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Data layer chaos
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Replication lag
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What the interviewer is testing with:&lt;/strong&gt; "Your read replicas are falling 8 seconds behind the primary. Which users notice, and what do they experience?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What replication lag shows:&lt;/strong&gt; Write-then-read flows break first. A user updates their profile photo and immediately refreshes. The replica serves the old photo.&lt;/p&gt;

&lt;p&gt;The design decision it tests is whether you have identified which flows require reading from the primary versus which can tolerate eventual consistency. Most designs treat all reads as replica-safe. Replication lag reveals which ones are not.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connection pool exhaustion
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What the interviewer is testing with:&lt;/strong&gt; "Your database is running at 30% CPU but your application is throwing connection timeout errors. What's happening?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What connection pool exhaustion shows:&lt;/strong&gt; A database can be entirely healthy while the application layer is unable to use it, because the connection pool is full of connections that are slow to complete.&lt;/p&gt;

&lt;p&gt;This scenario teaches that pool utilization is a more important leading indicator than database CPU. The design decision it tests is how you size pools and whether you have pool utilization alerts before exhaustion, not after.&lt;/p&gt;

&lt;h2&gt;
  
  
  Traffic chaos
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Request spike
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What the interviewer is testing with:&lt;/strong&gt; "Your service goes viral. Traffic goes from 5,000 to 50,000 RPS in four minutes. What breaks first?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What a request spike shows:&lt;/strong&gt; The first component to saturate is always the one with the smallest capacity headroom, which is often not the component you expected. At 10x traffic, the cache usually holds because hit rate stays high. The database write path saturates first if writes are not queued.&lt;/p&gt;

&lt;p&gt;Auto-scaling helps, but it has a lag. The design decision it tests is what happens between the spike starting and the new capacity coming online.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thundering herd
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What the interviewer is testing with:&lt;/strong&gt; "Your service goes down for 30 seconds and comes back up. All your clients try to reconnect at the same moment. What happens?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What thundering herd shows:&lt;/strong&gt; Coordinated load is more damaging than the same volume of load spread over time. A million clients reconnecting in the same two-second window produce a load spike that no steady-state capacity planning accounts for.&lt;/p&gt;

&lt;p&gt;The design decisions it tests are jittered reconnection backoff on the client side and request coalescing or rate limiting on the server side.&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%2F5okd3r5it3cc45jrcp30.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%2F5okd3r5it3cc45jrcp30.png" alt="SysSimulator" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to narrate a chaos scenario in an interview
&lt;/h2&gt;

&lt;p&gt;The structure that works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;State the steady state first.&lt;/li&gt;
&lt;li&gt;Describe the injection.&lt;/li&gt;
&lt;li&gt;Walk through the cascade in order.&lt;/li&gt;
&lt;li&gt;Give specific numbers.&lt;/li&gt;
&lt;li&gt;State your design fix.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Steady state:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"At 30K RPS with a warm cache, p99 is 48ms and error rate is 0.1%."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Injection:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The cache restarts, so hit rate drops to near zero."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cascade:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Origin requests spike by a factor of 10. The database connection pool is sized for 3,000 concurrent connections. At 30K RPS hitting origin, that pool exhausts in under 500 milliseconds."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Numbers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"p99 climbs to 2,400ms. Error rate hits 34%."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fix:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Request coalescing at the cache layer means a thousand simultaneous misses for the same key produce one origin request. Probabilistic early expiration means the cache never goes fully cold during a deploy."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The numbers are what make the answer credible. Abstract descriptions of cascade failure sound like everyone else's answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to actually practice this
&lt;/h2&gt;

&lt;p&gt;Reading about cache stampede and watching it happen on a live metrics graph are different experiences.&lt;/p&gt;

&lt;p&gt;I built a free browser-based &lt;a href="https://syssimulator.com/chaos-engineering" rel="noopener noreferrer"&gt;chaos engineering simulator with all 28 scenarios&lt;/a&gt;: load any blueprint, run traffic at your target RPS, inject a chaos scenario, and watch p99, error rate, and throughput change in real time. No infrastructure, no signup, runs entirely in the browser.&lt;/p&gt;

&lt;p&gt;The cache stampede on the Twitter / X Clone blueprint is the most instructive one to start with.&lt;/p&gt;

&lt;h2&gt;
  
  
  The difference is familiarity
&lt;/h2&gt;

&lt;p&gt;The candidates who answer chaos questions well are not smarter. They have seen the failure mode before.&lt;/p&gt;

&lt;p&gt;They know that connection pool exhaustion, not database CPU, is what kills a cache stampede. They know that latency injection multiplies through synchronous call chains. They know that thundering herd hits hardest in the two seconds after recovery, not during the outage.&lt;/p&gt;

&lt;p&gt;That familiarity is the difference between a hand-wave and a convincing answer, and it is entirely learnable before the interview.&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>career</category>
      <category>interview</category>
      <category>devops</category>
    </item>
    <item>
      <title>Why your AWS architecture cost estimate is always wrong - and how to fix it</title>
      <dc:creator>Ronit Dahiya</dc:creator>
      <pubDate>Sat, 06 Jun 2026 15:57:16 +0000</pubDate>
      <link>https://dev.to/ronitdahiya/why-your-aws-architecture-cost-estimate-is-always-wrong-and-how-to-fix-it-1d2e</link>
      <guid>https://dev.to/ronitdahiya/why-your-aws-architecture-cost-estimate-is-always-wrong-and-how-to-fix-it-1d2e</guid>
      <description>&lt;p&gt;$12,000 per month or $1,800. Same architecture. Same traffic. Same instance types. One architecture decision different.&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%2Fhjz38pjzxktuztvo3bu0.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%2Fhjz38pjzxktuztvo3bu0.png" alt="E Commerce Website SysSimulator" width="800" height="361"&gt;&lt;/a&gt;&lt;br&gt;
That decision is whether you put a CDN in front of your origin. Not a specific vendor button. Not a pricing-calculator checkbox. Just the architectural choice to add an edge cache between users and the services that would otherwise serve every read directly.&lt;/p&gt;

&lt;p&gt;The reason most cost estimates miss this is that most estimates start from the wrong place: service configuration instead of architecture.&lt;/p&gt;

&lt;p&gt;Here is what typically happens. An engineer needs to estimate AWS costs for a new service. They open the AWS Pricing Calculator, select EC2, guess an instance type, type in some hours. Then RDS, ElastiCache, maybe a load balancer. They add it up, present the number, and move on.&lt;/p&gt;

&lt;p&gt;The problem is that by the time you are filling in that calculator, you have already made the three or four architectural decisions that determine whether the bill is reasonable or catastrophic. The calculator is just arithmetic on top of those decisions. If the decisions are wrong, precise arithmetic makes things worse, not better.&lt;/p&gt;

&lt;h2&gt;
  
  
  The line item everyone underestimates
&lt;/h2&gt;

&lt;p&gt;Data transfer out is the most expensive surprise in AWS billing. It does not look expensive per unit. A few cents per GB sounds harmless until you do the multiplication.&lt;/p&gt;

&lt;p&gt;A standard web application at 10,000 requests per second with an average response size of 5KB produces roughly 130 terabytes of outbound transfer per month.&lt;/p&gt;

&lt;p&gt;That math is simple: 10,000 RPS x 5KB x 2,592,000 seconds per month. At roughly $0.09/GB, that is around $11,700 per month from data transfer alone, before you have paid for a single EC2 instance, database, cache, or queue.&lt;/p&gt;

&lt;p&gt;This is not an edge case. Any application with meaningful read traffic and no CDN layer runs into this wall. The application tier, the database, and the cache are usually not the expensive part. The pipe is.&lt;/p&gt;

&lt;p&gt;Now put a CDN component in front of the same origin. The CDN serves cached static assets, media, pages, or API responses from the edge when possible. The origin only handles cache misses and dynamic requests.&lt;/p&gt;

&lt;p&gt;If the CDN absorbs 85 to 90 percent of read traffic, origin transfer drops by the same order of magnitude. The architecture did not need a new database. It did not need a bigger app server. It needed the read path to stop treating the origin as the first stop for every request.&lt;/p&gt;

&lt;p&gt;That is the point most estimates miss. The CDN is not just a performance optimization with a cost. At scale, it is often a cost-control decision that also improves latency.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three decisions that set your bill before you open any calculator
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Compute model.&lt;/strong&gt; EC2, ECS Fargate, and Lambda have fundamentally different cost structures at different request rates. Lambda charges per invocation and duration. That can be cheap at low, intermittent traffic and expensive at sustained high volume. The crossover point where EC2 or containers become cheaper than Lambda is usually workload-specific, but it often appears once traffic becomes steady rather than spiky.&lt;/p&gt;

&lt;p&gt;Most teams pick one model and stick with it without checking where that crossover sits for their workload. Checking it before you build costs nothing. Discovering it after months of high bills is more expensive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data path.&lt;/strong&gt; This is the CDN question, but it is broader than CDN alone. Are clients pulling directly from app servers, or can static assets and media come from a CDN? Are large files proxied through compute, or served from object storage? Are repeat reads hitting a cache, or falling through to the database?&lt;/p&gt;

&lt;p&gt;These choices decide whether scale multiplies expensive origin work or gets absorbed closer to the user.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Database tier.&lt;/strong&gt; RDS, Aurora Serverless, DynamoDB, and cache-backed database patterns all have different cost curves. A steady 24/7 API has a different answer than a batch-heavy internal tool or a bursty consumer product. The right database estimate depends on write rate, read rate, storage growth, replication, and how much traffic can be served from cache.&lt;/p&gt;

&lt;p&gt;The wrong database tier can cost more than the wrong instance type. The wrong read path can cost more than both.&lt;/p&gt;

&lt;h2&gt;
  
  
  Estimate before the architecture is locked, not after
&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%2Fp4fos99lykil5u6gmqn9.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%2Fp4fos99lykil5u6gmqn9.png" alt="Youtube System Design SysSimulator" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The most useful cost estimate is not "what will this cost at our current traffic." It is "what will this cost at 2x, 5x, and 10x traffic, and where does the curve bend?"&lt;/p&gt;

&lt;p&gt;The point where cost growth accelerates tells you where the first architectural change will be required. Sometimes it is the database. Sometimes it is the cache. Sometimes it is data transfer because every request is still going back to origin.&lt;/p&gt;

&lt;p&gt;Almost never is it only the compute instance type, which is the part engineers spend the most time optimizing.&lt;/p&gt;

&lt;p&gt;Running this estimate before committing to an architecture means you design for the right bottleneck. Running it after means you redesign under pressure when the bill arrives.&lt;/p&gt;

&lt;p&gt;I built a free browser-based cost estimator that starts from your architecture diagram rather than service configuration: you draw components like Load Balancer, App Server, Cache, Database, Object Storage, and CDN, set your target RPS, and see the cost breakdown update from the diagram in real time with no account required: &lt;a href="https://syssimulator.com/aws-cost-estimator" rel="noopener noreferrer"&gt;https://syssimulator.com/aws-cost-estimator&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The bill is set before the calculator opens
&lt;/h2&gt;

&lt;p&gt;Cost estimation done after architectural decisions are made is just arithmetic on choices you have already locked in. The CDN question, the compute model question, the data path question, and the database tier question are what move the bill by factors of two to ten.&lt;/p&gt;

&lt;p&gt;Instance type optimization still matters, but it usually moves the number by tens of percent. Architecture moves it by orders of magnitude.&lt;/p&gt;

&lt;p&gt;Estimate while the architecture is still fluid. Run the numbers at 2x, 5x, and 10x. Find where the curve bends. That is where your first architectural constraint lives, and it is much cheaper to discover it on a diagram than in production.&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>aws</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <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>
