<?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: Othmane</title>
    <description>The latest articles on DEV Community by Othmane (@othmanez05).</description>
    <link>https://dev.to/othmanez05</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%2F4122150%2F31cf16b2-232f-4c61-b5ec-3702d2263034.png</url>
      <title>DEV Community: Othmane</title>
      <link>https://dev.to/othmanez05</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/othmanez05"/>
    <language>en</language>
    <item>
      <title>I made a Redis cache fast enough to hide a bug</title>
      <dc:creator>Othmane</dc:creator>
      <pubDate>Thu, 24 Sep 2026 19:27:30 +0000</pubDate>
      <link>https://dev.to/othmanez05/i-made-a-redis-cache-fast-enough-to-hide-a-bug-389j</link>
      <guid>https://dev.to/othmanez05/i-made-a-redis-cache-fast-enough-to-hide-a-bug-389j</guid>
      <description>&lt;p&gt;I made a Redis cache fast enough to hide a bug.&lt;/p&gt;

&lt;p&gt;The application was a small bookstore. Every uncached request deliberately spent 1.5 seconds in PostgreSQL before returning the catalog.&lt;/p&gt;

&lt;p&gt;Then I added cache-aside with a 180-second TTL.&lt;/p&gt;

&lt;p&gt;The next request was fast. I inserted a new book into PostgreSQL, refreshed the page, and the book was still missing. The database was correct. Every container was healthy. The customer was reading the old catalog.&lt;/p&gt;

&lt;p&gt;I built that failure into a Torollo roadmap on purpose.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwa0e8rth11r8m077jh88.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwa0e8rth11r8m077jh88.gif" alt="&gt; Torollo cache-aside walkthrough: the HTTP check fails because Redis serves a stale catalog, DEL cache:books invalidates the entry, and the repeated check finds The Phoenix Project." width="720" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The cache-aside path
&lt;/h2&gt;

&lt;p&gt;The application checks Redis before touching PostgreSQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;CACHE_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache:books&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;CACHE_TTL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;

&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CACHE_KEY&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;body&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT pg_sleep(1.5);&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT title, author FROM books ORDER BY id;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;render_catalog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CACHE_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CACHE_TTL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first request misses the cache, queries PostgreSQL and stores the rendered catalog.&lt;/p&gt;

&lt;p&gt;The next request finds &lt;code&gt;cache:books&lt;/code&gt; and returns it without running the database query again.&lt;/p&gt;

&lt;p&gt;That is the part every cache-aside diagram explains. The more interesting part begins when the database changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The infrastructure was healthy
&lt;/h2&gt;

&lt;p&gt;I added a book directly to PostgreSQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;books&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'The Phoenix Project'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Gene Kim'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PostgreSQL now contained the new row. Redis still contained the HTML generated before the insert.&lt;/p&gt;

&lt;p&gt;Refreshing the store returned the cached HTML, so the new book remained invisible until one of two things happened:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The 180-second TTL expired
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEL cache:books
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Redis was not malfunctioning. It returned exactly the value the application had stored.&lt;/p&gt;

&lt;p&gt;The failure was in the invalidation strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking the configuration would miss the bug
&lt;/h2&gt;

&lt;p&gt;Torollo runs the web server, PostgreSQL and Redis as real Docker containers.&lt;/p&gt;

&lt;p&gt;A weak validator could check that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the Redis container is running;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cache:books&lt;/code&gt; exists;&lt;/li&gt;
&lt;li&gt;the key has a TTL.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every one of those checks would pass while the application displayed stale data.&lt;/p&gt;

&lt;p&gt;The roadmap instead sends an HTTP request to the running web application and checks the response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http_get_contains"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"params"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"web"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"expectedText"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Phoenix"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The validation fails because the user-visible result is wrong, even though the infrastructure looks healthy.&lt;/p&gt;

&lt;p&gt;After deleting &lt;code&gt;cache:books&lt;/code&gt;, the next request goes back to PostgreSQL, rebuilds the cached value and includes the new book. A second request confirms that Redis is serving the corrected catalog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Availability and freshness are different failures
&lt;/h2&gt;

&lt;p&gt;The application also treats Redis as an optimization.&lt;/p&gt;

&lt;p&gt;Redis calls have short timeouts. If the cache becomes unavailable, the application falls back to PostgreSQL. The page becomes slower, but it can still respond.&lt;/p&gt;

&lt;p&gt;That protects availability. It does not protect freshness.&lt;/p&gt;

&lt;p&gt;A healthy cache can return stale data much faster than a database can return the correct data. Monitoring container health or request latency alone will not expose that mistake.&lt;/p&gt;

&lt;p&gt;The behavior has to be tested.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce the failure locally
&lt;/h2&gt;

&lt;p&gt;Torollo is an open-source system-design lab backed by real local containers.&lt;/p&gt;

&lt;p&gt;Run it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx torollo start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;strong&gt;Learning&lt;/strong&gt;, choose &lt;strong&gt;Cache-aside with Redis&lt;/strong&gt;, and follow the exercise until the stale catalog step.&lt;/p&gt;

&lt;p&gt;The source is available on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Derssa/Torollo" rel="noopener noreferrer"&gt;https://github.com/Derssa/Torollo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When a database write changes several cached views, where do you perform invalidation in your systems: inside the write path, through an event consumer, or by accepting a bounded TTL window?&lt;/p&gt;

</description>
      <category>redis</category>
      <category>docker</category>
      <category>backend</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>I wanted a system-design lab that could prove me wrong</title>
      <dc:creator>Othmane</dc:creator>
      <pubDate>Sat, 12 Sep 2026 12:57:23 +0000</pubDate>
      <link>https://dev.to/othmanez05/i-wanted-a-system-design-lab-that-could-prove-me-wrong-5540</link>
      <guid>https://dev.to/othmanez05/i-wanted-a-system-design-lab-that-could-prove-me-wrong-5540</guid>
      <description>&lt;p&gt;When I practice system design, the part that has always felt a little unsatisfying is that the diagram usually gets the final say.&lt;/p&gt;

&lt;p&gt;You can draw a load balancer, put PostgreSQL behind your application servers, add a private subnet, and end up with something that looks perfectly reasonable.&lt;/p&gt;

&lt;p&gt;But the diagram itself never proves that PostgreSQL is actually private.&lt;/p&gt;

&lt;p&gt;That is the idea I kept coming back to while building &lt;strong&gt;Torollo&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What if the diagram was only the intended architecture, and the running system was the thing that decided whether you were right?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That became the core of the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture actually runs
&lt;/h2&gt;

&lt;p&gt;Torollo is a local system-design lab.&lt;/p&gt;

&lt;p&gt;You build an architecture visually, but the nodes on the canvas are backed by real Docker resources on your machine.&lt;/p&gt;

&lt;p&gt;If you add a server, there is a container behind it.&lt;/p&gt;

&lt;p&gt;If an exercise uses PostgreSQL, PostgreSQL is actually running.&lt;/p&gt;

&lt;p&gt;If the architecture introduces network boundaries, Torollo has real Docker networking underneath them.&lt;/p&gt;

&lt;p&gt;The roadmap then validates the environment that exists instead of checking whether your diagram looks correct.&lt;/p&gt;

&lt;p&gt;One of the free roadmaps, for example, has you build a resilient three-tier application.&lt;/p&gt;

&lt;p&gt;You start with a web server and a database, then progressively introduce:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a load balancer&lt;/li&gt;
&lt;li&gt;multiple web servers&lt;/li&gt;
&lt;li&gt;network restrictions&lt;/li&gt;
&lt;li&gt;failure scenarios&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At one point, PostgreSQL is supposed to be isolated from the load balancer.&lt;/p&gt;

&lt;p&gt;Torollo actually checks that.&lt;/p&gt;

&lt;p&gt;If the load balancer can still reach PostgreSQL on port &lt;code&gt;5432&lt;/code&gt;, the step fails.&lt;/p&gt;

&lt;p&gt;You change the configuration, run the validator again, and the step only turns green when that connection is really blocked.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff8wf0c3bgjstep025lz7.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff8wf0c3bgjstep025lz7.gif" alt=" " width="720" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That loop ended up becoming the part of Torollo I care about the most:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Build something → make an assumption → let the runtime prove whether the assumption is true.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The canvas is only one layer
&lt;/h2&gt;

&lt;p&gt;The frontend is built with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;React Flow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The backend uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js&lt;/li&gt;
&lt;li&gt;Express&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;Dockerode&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But I try to keep a strict separation between what the canvas says exists and what Torollo can actually observe.&lt;/p&gt;

&lt;p&gt;If the frontend says a server exists, that is not enough for a validator.&lt;/p&gt;

&lt;p&gt;The validator can inspect whether the container is actually running.&lt;/p&gt;

&lt;p&gt;If an architecture says an application can reach its database, Torollo can test that connection.&lt;/p&gt;

&lt;p&gt;If a roadmap asks you to create a table, the validator can inspect PostgreSQL.&lt;/p&gt;

&lt;p&gt;If the exercise uses Redis, it can inspect actual Redis state.&lt;/p&gt;

&lt;p&gt;That was an important design decision for me, because otherwise Torollo would just become another graph editor with a green checkmark attached to it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The runtime has to be able to disagree with the diagram.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Docker networking made this much harder
&lt;/h2&gt;

&lt;p&gt;The container lifecycle was relatively straightforward.&lt;/p&gt;

&lt;p&gt;Networking was not.&lt;/p&gt;

&lt;p&gt;Some Torollo labs use Docker bridge networks, routing, NAT and &lt;code&gt;iptables&lt;/code&gt; rules to make the architecture behave differently depending on how you configure it.&lt;/p&gt;

&lt;p&gt;That is where I started running into the fact that "runs on Docker" does not mean every Docker environment behaves identically.&lt;/p&gt;

&lt;p&gt;Rootless Docker is a good example.&lt;/p&gt;

&lt;p&gt;It can run the containers, but some of the advanced networking behavior Torollo uses requires capabilities that a rootless daemon cannot provide in the same way.&lt;/p&gt;

&lt;p&gt;Initially, I wanted to hide details like that from the learner.&lt;/p&gt;

&lt;p&gt;I eventually decided that was the wrong abstraction.&lt;/p&gt;

&lt;p&gt;Torollo is specifically trying to make infrastructure behavior tangible. Pretending that the underlying environment does not matter would work against that.&lt;/p&gt;

&lt;p&gt;So the CLI now includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx torollo doctor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It checks the Docker environment, ports and some runtime requirements and tries to explain what is wrong before you spend time debugging an exercise that your machine cannot execute properly.&lt;/p&gt;

&lt;p&gt;That feature came directly from repeatedly debugging what looked like Torollo failures and discovering that the actual problem was the environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  I’m not trying to recreate AWS locally
&lt;/h2&gt;

&lt;p&gt;This is probably the easiest trap for a project like Torollo to fall into.&lt;/p&gt;

&lt;p&gt;Once you have containers, databases, load balancing, queues, caching and networking, there is always another infrastructure primitive you could implement.&lt;/p&gt;

&lt;p&gt;But I don’t want Torollo to become a fake cloud provider.&lt;/p&gt;

&lt;p&gt;The goal is much narrower:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Take an architecture concept and make enough of it real that the learner can be wrong about it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you say a database is isolated, there should be a way to test that claim.&lt;/p&gt;

&lt;p&gt;If you introduce a cache, it should contain real state.&lt;/p&gt;

&lt;p&gt;If a worker is supposed to process a queue, there should be an actual worker and queue involved.&lt;/p&gt;

&lt;p&gt;If a failure is part of the exercise, something should really fail.&lt;/p&gt;

&lt;p&gt;The local environment is there to make the consequences of the architecture visible, not to pretend that Docker on a laptop is equivalent to production infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Torollo includes right now
&lt;/h2&gt;

&lt;p&gt;The current public version has three free roadmaps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a resilient three-tier application&lt;/li&gt;
&lt;li&gt;Redis cache-aside&lt;/li&gt;
&lt;li&gt;Redis queue workers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The roadmap format is JSON, so the exercises are not hardcoded into the application.&lt;/p&gt;

&lt;p&gt;The core is &lt;strong&gt;MIT licensed&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It runs locally.&lt;/p&gt;

&lt;p&gt;There is &lt;strong&gt;no account requirement&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You currently need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node 18+&lt;/li&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/Derssa/Torollo" rel="noopener noreferrer"&gt;https://github.com/Derssa/Torollo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three-tier roadmap:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://torollo.app/r/devto" rel="noopener noreferrer"&gt;https://torollo.app/r/devto&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The question I’m trying to answer next
&lt;/h2&gt;

&lt;p&gt;I think making the runtime real improves some parts of learning system design.&lt;/p&gt;

&lt;p&gt;"PostgreSQL is private" stops being something written next to a box and becomes a statement that can actually be false.&lt;/p&gt;

&lt;p&gt;But there is obviously a limit.&lt;/p&gt;

&lt;p&gt;A local Docker environment does not reproduce IAM, managed services, cloud control planes, real latency, production observability, regional failures and all the other things that shape real systems.&lt;/p&gt;

&lt;p&gt;So the question I’m trying to understand now is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Where does making the runtime real improve the way you learn system design, and where does it start creating the wrong mental model?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you work with backend systems, Docker, DevOps, SRE or infrastructure, that is the feedback I would be most interested in.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>docker</category>
      <category>opensource</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
