<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[The Polymathic Engineer]]></title><description><![CDATA[A newsletter about Algorithms, Computer Vision, and Distributed Systems]]></description><link>https://newsletter.francofernando.com</link><image><url>https://substackcdn.com/image/fetch/$s_!WkEZ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F157b59b0-a7e4-4f31-8d83-9a2034b2ff4e_354x354.png</url><title>The Polymathic Engineer</title><link>https://newsletter.francofernando.com</link></image><generator>Substack</generator><lastBuildDate>Tue, 14 Apr 2026 18:20:47 GMT</lastBuildDate><atom:link href="https://newsletter.francofernando.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Franco Fernando]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[francofernando@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[francofernando@substack.com]]></itunes:email><itunes:name><![CDATA[Franco Fernando]]></itunes:name></itunes:owner><itunes:author><![CDATA[Franco Fernando]]></itunes:author><googleplay:owner><![CDATA[francofernando@substack.com]]></googleplay:owner><googleplay:email><![CDATA[francofernando@substack.com]]></googleplay:email><googleplay:author><![CDATA[Franco Fernando]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[The Dictionary Problem: Fast Lookups in Large Collections]]></title><description><![CDATA[From simple arrays to hash tables and BSTs: exploring the trade-offs behind one of the most common problems in computer science.]]></description><link>https://newsletter.francofernando.com/p/the-dictionary-problem-fast-lookups</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/the-dictionary-problem-fast-lookups</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Sat, 11 Apr 2026 09:32:49 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/0eea31a5-227a-48a7-b58d-db9a23cbec64_814x412.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends,</p><p>Welcome to the 168th issue of the Polymathic Engineer newsletter.</p><p>Let&#8217;s say you are building a spell checker that runs directly in the browser. You have a dictionary of over 100,000 words, and every time the user types something, you need to quickly check if the word exists in your dictionary. If it doesn&#8217;t, you underline it in red.</p><p>The feature sounds easy, but there is a problem: how can you store and search through more than 100,000 words quickly? It would be too slow to send a request to the server for every single word. You need to maintain the dictionary locally and search through it fast, without consuming too much memory.</p><p>This is a classic problem in computer science, since it comes up all the time: checking if a username is already taken or if an email is in a contacts list, or verifying whether a URL exists in a cache are all good examples. The main point is always the same: what is the most efficient way to check if something is in a large collection of items?</p><p>In this article, we will explore several data structures that solve this problem, starting from the simplest approach and working our way up to more sophisticated solutions. Along the way, we will see how each one makes different trade-offs in terms of speed, memory, and flexibility. This is the outline:</p><ul><li><p>Abstract data types</p></li><li><p>Unsorted arrays</p></li><li><p>Sorted arrays and binary search</p></li><li><p>Hash tables</p></li><li><p>Binary search trees</p></li><li><p>Comparing the options</p></li></ul><div><hr></div><h4><strong><a href="https://coderabbit.link/fernando">Your AI shouldn't grade its own homework</a></strong></h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oBf2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66478278-656b-4112-a0f1-49bb03ea0435_1558x936.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oBf2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66478278-656b-4112-a0f1-49bb03ea0435_1558x936.png 424w, https://substackcdn.com/image/fetch/$s_!oBf2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66478278-656b-4112-a0f1-49bb03ea0435_1558x936.png 848w, https://substackcdn.com/image/fetch/$s_!oBf2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66478278-656b-4112-a0f1-49bb03ea0435_1558x936.png 1272w, https://substackcdn.com/image/fetch/$s_!oBf2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66478278-656b-4112-a0f1-49bb03ea0435_1558x936.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oBf2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66478278-656b-4112-a0f1-49bb03ea0435_1558x936.png" width="608" height="365.38461538461536" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/66478278-656b-4112-a0f1-49bb03ea0435_1558x936.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:875,&quot;width&quot;:1456,&quot;resizeWidth&quot;:608,&quot;bytes&quot;:86772,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/193795555?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66478278-656b-4112-a0f1-49bb03ea0435_1558x936.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!oBf2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66478278-656b-4112-a0f1-49bb03ea0435_1558x936.png 424w, https://substackcdn.com/image/fetch/$s_!oBf2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66478278-656b-4112-a0f1-49bb03ea0435_1558x936.png 848w, https://substackcdn.com/image/fetch/$s_!oBf2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66478278-656b-4112-a0f1-49bb03ea0435_1558x936.png 1272w, https://substackcdn.com/image/fetch/$s_!oBf2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66478278-656b-4112-a0f1-49bb03ea0435_1558x936.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Claude Code writes beautiful code. So does Codex. But here&#8217;s the thing, they also think they write beautiful code. And when you ask an AI to review code it just wrote, you get the intellectual equivalent of a student grading their own exam. Shockingly, they always pass.</p><p><a href="https://coderabbit.link/fernando">CodeRabbit CLI</a> plugs into Claude Code and Codex as an external reviewer, different AI Agent, different architecture, 40+ static analyzers and zero emotional attachment to the code it&#8217;s looking at. The agent writes, CodeRabbit reviews, and the agent fixes. Loop until clean.</p><p>You show up when there&#8217;s actually something worth approving.</p><p>One command. Autonomous generate-review-iterate cycles. The AI still does the work. It just doesn&#8217;t get to decide if the work is good anymore.</p><p><a href="https://coderabbit.link/fernando">Free tier available. Try CodeRabbit's CLI.</a></p><p>Thanks to the CodeRabbit team for collaborating with me on this newsletter issue.</p><div><hr></div><h2>Abstract data types</h2><p>Before jumping into specific solutions for the dictionary problem, it is worth taking a step back and talking about abstract data types.</p><p>An abstract data type (ADT) defines what operations a data structure should support, without saying anything about how those operations are really done. It is a contract: you know what you can do with it, but the internal details are hidden.</p><p>The reason why it&#8217;s important to distinguish between abstract data types and concrete data structures is that it forces you to figure out what you need before thinking about how to make it work.</p><p>The ADT we care about here is the dictionary, which is also known as a symbol table, associative array, or map. The ADT dictionary keeps a collection of (key, value) pairs and supports three operations:</p><ul><li><p><strong>insert(key, value)</strong>: adds a new pair to the collection</p></li><li><p><strong>remove(key)</strong>: removes the pair associated with the given key</p></li><li><p><strong>contains(key)</strong>: checks if a key exists and returns the associated value</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KPNP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5acbb453-0187-4ab2-95a0-d14370cc1a34_814x412.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KPNP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5acbb453-0187-4ab2-95a0-d14370cc1a34_814x412.png 424w, https://substackcdn.com/image/fetch/$s_!KPNP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5acbb453-0187-4ab2-95a0-d14370cc1a34_814x412.png 848w, https://substackcdn.com/image/fetch/$s_!KPNP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5acbb453-0187-4ab2-95a0-d14370cc1a34_814x412.png 1272w, https://substackcdn.com/image/fetch/$s_!KPNP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5acbb453-0187-4ab2-95a0-d14370cc1a34_814x412.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!KPNP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5acbb453-0187-4ab2-95a0-d14370cc1a34_814x412.png" width="625" height="316.33906633906633" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5acbb453-0187-4ab2-95a0-d14370cc1a34_814x412.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:412,&quot;width&quot;:814,&quot;resizeWidth&quot;:625,&quot;bytes&quot;:164434,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/193795555?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5acbb453-0187-4ab2-95a0-d14370cc1a34_814x412.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!KPNP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5acbb453-0187-4ab2-95a0-d14370cc1a34_814x412.png 424w, https://substackcdn.com/image/fetch/$s_!KPNP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5acbb453-0187-4ab2-95a0-d14370cc1a34_814x412.png 848w, https://substackcdn.com/image/fetch/$s_!KPNP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5acbb453-0187-4ab2-95a0-d14370cc1a34_814x412.png 1272w, https://substackcdn.com/image/fetch/$s_!KPNP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5acbb453-0187-4ab2-95a0-d14370cc1a34_814x412.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The most crucial thing is that there is only one copy of each key in the collection, and you may get any value directly by using its key. The easiest way to think about this is to consider a regular array as a special case.</p><p>In an array like [&#8221;the&#8221;, &#8221;lazy&#8221;, &#8221;fox&#8221;], the keys are simply the indices 0, 1, and 2, and you can always get a value by providing its position. A dictionary takes this idea further by letting you use keys from almost any domain, such as strings, numbers, or objects.</p><p>For the above-mentioned spell checker problem, we actually need a simpler version of the dictionary called a set. A set only knows if a key is there or not; it doesn&#8217;t store any values with the keys. You can think of it as a dictionary where values are always either true or false.</p><h2>Unsorted arrays</h2><p>The simplest way to solve our spell checker problem is to dump all 100,000 words into an unsorted array. You don't have to do anything special. You simply store the words in the order you get them.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7QB6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76854f3e-b8f7-49c0-a77c-20df58ebc7dc_393x105.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7QB6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76854f3e-b8f7-49c0-a77c-20df58ebc7dc_393x105.png 424w, https://substackcdn.com/image/fetch/$s_!7QB6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76854f3e-b8f7-49c0-a77c-20df58ebc7dc_393x105.png 848w, https://substackcdn.com/image/fetch/$s_!7QB6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76854f3e-b8f7-49c0-a77c-20df58ebc7dc_393x105.png 1272w, https://substackcdn.com/image/fetch/$s_!7QB6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76854f3e-b8f7-49c0-a77c-20df58ebc7dc_393x105.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7QB6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76854f3e-b8f7-49c0-a77c-20df58ebc7dc_393x105.png" width="359" height="95.91603053435115" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/76854f3e-b8f7-49c0-a77c-20df58ebc7dc_393x105.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:105,&quot;width&quot;:393,&quot;resizeWidth&quot;:359,&quot;bytes&quot;:56843,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/193795555?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76854f3e-b8f7-49c0-a77c-20df58ebc7dc_393x105.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7QB6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76854f3e-b8f7-49c0-a77c-20df58ebc7dc_393x105.png 424w, https://substackcdn.com/image/fetch/$s_!7QB6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76854f3e-b8f7-49c0-a77c-20df58ebc7dc_393x105.png 848w, https://substackcdn.com/image/fetch/$s_!7QB6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76854f3e-b8f7-49c0-a77c-20df58ebc7dc_393x105.png 1272w, https://substackcdn.com/image/fetch/$s_!7QB6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76854f3e-b8f7-49c0-a77c-20df58ebc7dc_393x105.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>It is easy to add a new word since you can just put it at the end of the array. That takes O(1) time on average. If you don't care about keeping things in order, it is also quick to remove a word. You just swap it with the last element and make the array smaller.</p><p>However, things fall apart when it comes to search. If someone types "algorithm" and you need to see whether it is in the dictionary, the only option is go through the array word by word, starting at the beginning and proceeding until you find it or reach the end. In the worst case, that is O(n), which means that for 100,000 words, you may have to look at all the 100,000 entries before you can notify the user that the word is spelled correctly.</p><p>Now imagine doing this for every single word the user types. A short paragraph of 200 words would mean up to 20 million comparisons in the worst case. That is not going to work for a feature that needs to feel instant.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PVEr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec551de7-ce3e-4e26-903d-084ff4a72a3d_1546x475.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PVEr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec551de7-ce3e-4e26-903d-084ff4a72a3d_1546x475.png 424w, https://substackcdn.com/image/fetch/$s_!PVEr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec551de7-ce3e-4e26-903d-084ff4a72a3d_1546x475.png 848w, https://substackcdn.com/image/fetch/$s_!PVEr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec551de7-ce3e-4e26-903d-084ff4a72a3d_1546x475.png 1272w, https://substackcdn.com/image/fetch/$s_!PVEr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec551de7-ce3e-4e26-903d-084ff4a72a3d_1546x475.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PVEr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec551de7-ce3e-4e26-903d-084ff4a72a3d_1546x475.png" width="650" height="199.55357142857142" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ec551de7-ce3e-4e26-903d-084ff4a72a3d_1546x475.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:447,&quot;width&quot;:1456,&quot;resizeWidth&quot;:650,&quot;bytes&quot;:41164,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/193795555?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec551de7-ce3e-4e26-903d-084ff4a72a3d_1546x475.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!PVEr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec551de7-ce3e-4e26-903d-084ff4a72a3d_1546x475.png 424w, https://substackcdn.com/image/fetch/$s_!PVEr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec551de7-ce3e-4e26-903d-084ff4a72a3d_1546x475.png 848w, https://substackcdn.com/image/fetch/$s_!PVEr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec551de7-ce3e-4e26-903d-084ff4a72a3d_1546x475.png 1272w, https://substackcdn.com/image/fetch/$s_!PVEr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec551de7-ce3e-4e26-903d-084ff4a72a3d_1546x475.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The one good thing about the unsorted array is that it is simple. No extra memory, no upfront cost, and insertion is as fast as it gets. But the O(n) lookup makes it useless for anything beyond very small collections. We need a faster way to search.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.francofernando.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">The Polymathic Engineer is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Sorted arrays and binary search</h2><p>What if we sort the array first? We could take advantage of the ordering to find things much faster instead of scanning word by word.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!R9QW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71cce44d-1f56-4e50-9958-c8018f9478c7_393x106.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!R9QW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71cce44d-1f56-4e50-9958-c8018f9478c7_393x106.png 424w, https://substackcdn.com/image/fetch/$s_!R9QW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71cce44d-1f56-4e50-9958-c8018f9478c7_393x106.png 848w, https://substackcdn.com/image/fetch/$s_!R9QW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71cce44d-1f56-4e50-9958-c8018f9478c7_393x106.png 1272w, https://substackcdn.com/image/fetch/$s_!R9QW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71cce44d-1f56-4e50-9958-c8018f9478c7_393x106.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!R9QW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71cce44d-1f56-4e50-9958-c8018f9478c7_393x106.png" width="393" height="106" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/71cce44d-1f56-4e50-9958-c8018f9478c7_393x106.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:106,&quot;width&quot;:393,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:57073,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/193795555?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71cce44d-1f56-4e50-9958-c8018f9478c7_393x106.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!R9QW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71cce44d-1f56-4e50-9958-c8018f9478c7_393x106.png 424w, https://substackcdn.com/image/fetch/$s_!R9QW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71cce44d-1f56-4e50-9958-c8018f9478c7_393x106.png 848w, https://substackcdn.com/image/fetch/$s_!R9QW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71cce44d-1f56-4e50-9958-c8018f9478c7_393x106.png 1272w, https://substackcdn.com/image/fetch/$s_!R9QW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71cce44d-1f56-4e50-9958-c8018f9478c7_393x106.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>You probably already do this without thinking about it. When you look up a word in a paper dictionary, you don&#8217;t read every entry in a paper dictionary from the beginning. You open it somewhere in the middle, look at the first word on that page, and choose whether to go ahead or backward. If you are looking for &#8220;binary&#8221; and you land on the page starting with &#8220;method,&#8221; you know you can ignore everything after that page and focus on the first half.</p><p>This is exactly how binary search works. You start in the middle of the array, compare the target word with the word at that position, and then discard half of the remaining elements based on the result. Then you repeat the process on the surviving half. Each step cuts the search space in two, so it takes at most O(log n) comparisons to find any word. For 100,000 words, that is circa 17 comparisons, which is a huge improvement with respect to the unsorted array.</p><p>The problem is that keeping the array sorted comes at a cost. It takes O(nlogn) time to sort it in the first place, which is acceptable if you only do it once. But you have to find the correct place for the new word and move everything that comes after it every time you want to add a new word. That is O(n) for each insertion. Removing a word has the same issue because you need to shift elements to fill the gap.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GhY-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e10cb5e-ba4a-4652-9aa5-c41046ecc0b5_1528x490.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GhY-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e10cb5e-ba4a-4652-9aa5-c41046ecc0b5_1528x490.png 424w, https://substackcdn.com/image/fetch/$s_!GhY-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e10cb5e-ba4a-4652-9aa5-c41046ecc0b5_1528x490.png 848w, https://substackcdn.com/image/fetch/$s_!GhY-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e10cb5e-ba4a-4652-9aa5-c41046ecc0b5_1528x490.png 1272w, https://substackcdn.com/image/fetch/$s_!GhY-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e10cb5e-ba4a-4652-9aa5-c41046ecc0b5_1528x490.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GhY-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e10cb5e-ba4a-4652-9aa5-c41046ecc0b5_1528x490.png" width="617" height="197.89766483516485" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0e10cb5e-ba4a-4652-9aa5-c41046ecc0b5_1528x490.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:467,&quot;width&quot;:1456,&quot;resizeWidth&quot;:617,&quot;bytes&quot;:43192,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/193795555?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e10cb5e-ba4a-4652-9aa5-c41046ecc0b5_1528x490.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!GhY-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e10cb5e-ba4a-4652-9aa5-c41046ecc0b5_1528x490.png 424w, https://substackcdn.com/image/fetch/$s_!GhY-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e10cb5e-ba4a-4652-9aa5-c41046ecc0b5_1528x490.png 848w, https://substackcdn.com/image/fetch/$s_!GhY-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e10cb5e-ba4a-4652-9aa5-c41046ecc0b5_1528x490.png 1272w, https://substackcdn.com/image/fetch/$s_!GhY-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e10cb5e-ba4a-4652-9aa5-c41046ecc0b5_1528x490.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>If the dictionary doesn't change, a sorted array could be a reasonable solution for the spell checker example. But if the dictionary changes a lot, the O(n) cost of adding and removing words will be a problem. We need something that can quickly look up and add things at the same time.</p><h2>Hash tables</h2><p>The idea behind hash tables is simple. What if instead of searching through the array, we could compute exactly where a word should be stored?</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8vEL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d84928-26f0-4fea-aee0-367809aea271_762x429.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8vEL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d84928-26f0-4fea-aee0-367809aea271_762x429.png 424w, https://substackcdn.com/image/fetch/$s_!8vEL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d84928-26f0-4fea-aee0-367809aea271_762x429.png 848w, https://substackcdn.com/image/fetch/$s_!8vEL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d84928-26f0-4fea-aee0-367809aea271_762x429.png 1272w, https://substackcdn.com/image/fetch/$s_!8vEL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d84928-26f0-4fea-aee0-367809aea271_762x429.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8vEL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d84928-26f0-4fea-aee0-367809aea271_762x429.png" width="505" height="284.31102362204723" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b3d84928-26f0-4fea-aee0-367809aea271_762x429.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:429,&quot;width&quot;:762,&quot;resizeWidth&quot;:505,&quot;bytes&quot;:81277,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/193795555?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d84928-26f0-4fea-aee0-367809aea271_762x429.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!8vEL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d84928-26f0-4fea-aee0-367809aea271_762x429.png 424w, https://substackcdn.com/image/fetch/$s_!8vEL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d84928-26f0-4fea-aee0-367809aea271_762x429.png 848w, https://substackcdn.com/image/fetch/$s_!8vEL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d84928-26f0-4fea-aee0-367809aea271_762x429.png 1272w, https://substackcdn.com/image/fetch/$s_!8vEL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3d84928-26f0-4fea-aee0-367809aea271_762x429.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A hash table uses a hash function to do this. You give it a word, and it gives you back a number. This number is an index in an array. To insert the word &#8220;binary,&#8221; you run it through the hash function, get back something like 2,512, and store the word at that position. To check if &#8220;binary&#8221; is in the dictionary, you run the same function, look at position 4,712, and see if it is there. No scanning, no sorting, or comparing.</p><p>This makes both insertion and lookup O(1) on average, which is exactly what we were looking for. However, there is a catch. Since the hash function maps a very large set of possible words to a much smaller array, it is only natural that two different words will sometimes end up in the same position. This is what is called a collision. For example, both "binary" and "garden" might hash to index 2,512.</p><p>There are two common ways to handle collisions. With <strong>chaining</strong>, each position in the array has a short list, and you add colliding words to that list. With <strong>open addressing</strong>, you look for the next available slot in the array when a collision happens. Both ways work well as long as the array is large enough compared to the number of elements it holds.</p><p>One distinction to keep in mind is the one between hash maps and sets. You can use a hash map to link a value to a key, like putting a word and its meaning together. A hash set only keeps track of whether a key exists or not. We don't require definitions for the spell checkers; we just need to know if the word is there. All we need is a hash set.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Kl1C!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53b3e4b-538b-41a3-840e-f9b2e50839c3_1530x480.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Kl1C!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53b3e4b-538b-41a3-840e-f9b2e50839c3_1530x480.png 424w, https://substackcdn.com/image/fetch/$s_!Kl1C!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53b3e4b-538b-41a3-840e-f9b2e50839c3_1530x480.png 848w, https://substackcdn.com/image/fetch/$s_!Kl1C!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53b3e4b-538b-41a3-840e-f9b2e50839c3_1530x480.png 1272w, https://substackcdn.com/image/fetch/$s_!Kl1C!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53b3e4b-538b-41a3-840e-f9b2e50839c3_1530x480.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Kl1C!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53b3e4b-538b-41a3-840e-f9b2e50839c3_1530x480.png" width="678" height="212.8063186813187" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e53b3e4b-538b-41a3-840e-f9b2e50839c3_1530x480.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:457,&quot;width&quot;:1456,&quot;resizeWidth&quot;:678,&quot;bytes&quot;:49440,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/193795555?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53b3e4b-538b-41a3-840e-f9b2e50839c3_1530x480.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Kl1C!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53b3e4b-538b-41a3-840e-f9b2e50839c3_1530x480.png 424w, https://substackcdn.com/image/fetch/$s_!Kl1C!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53b3e4b-538b-41a3-840e-f9b2e50839c3_1530x480.png 848w, https://substackcdn.com/image/fetch/$s_!Kl1C!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53b3e4b-538b-41a3-840e-f9b2e50839c3_1530x480.png 1272w, https://substackcdn.com/image/fetch/$s_!Kl1C!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53b3e4b-538b-41a3-840e-f9b2e50839c3_1530x480.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>With O(1) on average for all the operations that matter, hash tables seem like the clear winner. But they come with a cost: memory. A hash table needs an array that is larger than the number of elements you plan to store, plus extra space to deal with collisions. </p><h2>Binary search trees</h2><p>A binary search tree (BST) takes a different approach. Instead of using a hash function to find where a word goes, it sorts words based on how they compare to each other.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!njTw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b147e2-19ed-4f4f-a090-549b463745b5_452x284.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!njTw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b147e2-19ed-4f4f-a090-549b463745b5_452x284.png 424w, https://substackcdn.com/image/fetch/$s_!njTw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b147e2-19ed-4f4f-a090-549b463745b5_452x284.png 848w, https://substackcdn.com/image/fetch/$s_!njTw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b147e2-19ed-4f4f-a090-549b463745b5_452x284.png 1272w, https://substackcdn.com/image/fetch/$s_!njTw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b147e2-19ed-4f4f-a090-549b463745b5_452x284.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!njTw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b147e2-19ed-4f4f-a090-549b463745b5_452x284.png" width="452" height="284" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d6b147e2-19ed-4f4f-a090-549b463745b5_452x284.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:284,&quot;width&quot;:452,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:39613,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/193795555?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b147e2-19ed-4f4f-a090-549b463745b5_452x284.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!njTw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b147e2-19ed-4f4f-a090-549b463745b5_452x284.png 424w, https://substackcdn.com/image/fetch/$s_!njTw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b147e2-19ed-4f4f-a090-549b463745b5_452x284.png 848w, https://substackcdn.com/image/fetch/$s_!njTw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b147e2-19ed-4f4f-a090-549b463745b5_452x284.png 1272w, https://substackcdn.com/image/fetch/$s_!njTw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6b147e2-19ed-4f4f-a090-549b463745b5_452x284.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A BST is a tree where each node holds a key, and for every node, all the keys in its left subtree are smaller and all the keys in its right subtree are larger. To add a new word, you start at the root and compare it to the current node. If the word is smaller, you go left. If it is larger, you go right. You keep doing this until you reach an empty spot, and that is where the word goes.</p><p>Lookup works similarly. To check if "binary" is in the tree, you start at the root and go down the comparisons. If you get to the word, it is there. If you get to an empty space, it isn't. The number of comparisons is equal to the height of the tree because you get one level deeper each time.</p><p>Things start to get interesting here. If the tree is balanced, its height is O(logn), which means that all operations, such as insertion, removal, and lookup, take O(logn) time. It is the same as binary search on sorted arrays, but it doesn't cost O(n) to add or remove items.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gO6v!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fb433e-c5bf-4f43-97db-bae72a7dbae6_1545x467.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gO6v!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fb433e-c5bf-4f43-97db-bae72a7dbae6_1545x467.png 424w, https://substackcdn.com/image/fetch/$s_!gO6v!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fb433e-c5bf-4f43-97db-bae72a7dbae6_1545x467.png 848w, https://substackcdn.com/image/fetch/$s_!gO6v!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fb433e-c5bf-4f43-97db-bae72a7dbae6_1545x467.png 1272w, https://substackcdn.com/image/fetch/$s_!gO6v!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fb433e-c5bf-4f43-97db-bae72a7dbae6_1545x467.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gO6v!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fb433e-c5bf-4f43-97db-bae72a7dbae6_1545x467.png" width="692" height="209.12087912087912" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d0fb433e-c5bf-4f43-97db-bae72a7dbae6_1545x467.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:440,&quot;width&quot;:1456,&quot;resizeWidth&quot;:692,&quot;bytes&quot;:44509,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/193795555?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fb433e-c5bf-4f43-97db-bae72a7dbae6_1545x467.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gO6v!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fb433e-c5bf-4f43-97db-bae72a7dbae6_1545x467.png 424w, https://substackcdn.com/image/fetch/$s_!gO6v!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fb433e-c5bf-4f43-97db-bae72a7dbae6_1545x467.png 848w, https://substackcdn.com/image/fetch/$s_!gO6v!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fb433e-c5bf-4f43-97db-bae72a7dbae6_1545x467.png 1272w, https://substackcdn.com/image/fetch/$s_!gO6v!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0fb433e-c5bf-4f43-97db-bae72a7dbae6_1545x467.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The main benefit of BSTs over hash tables is ordering. Since the words are organized by comparison, a BST can give you things like finding all words between &#8220;apple&#8221; and &#8220;banana,&#8221; or getting the word that comes right before or after a given one. In a hash table, these operations take O(n), whereas in a BST, they take O(log n).</p><p>For the spell checker, we don&#8217;t need any of that. But if you are building something like an autocomplete feature, where you need to find all words within a range, a BST is the better choice.</p><h2>Comparing the options</h2><p>Now that we have gone through the main data structures, let&#8217;s put them side by side and see how they compare.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Dndm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd112a473-dd85-4743-aa3d-ba011b5ab23a_1528x626.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Dndm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd112a473-dd85-4743-aa3d-ba011b5ab23a_1528x626.png 424w, https://substackcdn.com/image/fetch/$s_!Dndm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd112a473-dd85-4743-aa3d-ba011b5ab23a_1528x626.png 848w, https://substackcdn.com/image/fetch/$s_!Dndm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd112a473-dd85-4743-aa3d-ba011b5ab23a_1528x626.png 1272w, https://substackcdn.com/image/fetch/$s_!Dndm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd112a473-dd85-4743-aa3d-ba011b5ab23a_1528x626.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Dndm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd112a473-dd85-4743-aa3d-ba011b5ab23a_1528x626.png" width="630" height="258.3173076923077" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d112a473-dd85-4743-aa3d-ba011b5ab23a_1528x626.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:597,&quot;width&quot;:1456,&quot;resizeWidth&quot;:630,&quot;bytes&quot;:83273,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/193795555?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd112a473-dd85-4743-aa3d-ba011b5ab23a_1528x626.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Dndm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd112a473-dd85-4743-aa3d-ba011b5ab23a_1528x626.png 424w, https://substackcdn.com/image/fetch/$s_!Dndm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd112a473-dd85-4743-aa3d-ba011b5ab23a_1528x626.png 848w, https://substackcdn.com/image/fetch/$s_!Dndm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd112a473-dd85-4743-aa3d-ba011b5ab23a_1528x626.png 1272w, https://substackcdn.com/image/fetch/$s_!Dndm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd112a473-dd85-4743-aa3d-ba011b5ab23a_1528x626.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If all you need is to check whether an element is in a collection and don&#8217;t care about the order, hash tables are the clear winner. O(1) on average for lookup, insertion, and removal is hard to beat.</p><p>If order is important, BSTs are a better choice. A BST can let you find all the words in a range, get the next or previous element, or get the whole collection in sorted order in O(log n) time. Those operations would take O(n) or more time using a hash table.</p><p>Sorted arrays are a solid option when the data doesn&#8217;t change. You pay the O(n log n) cost once to sort, and then you get O(log n) lookups with no extra memory. But if you need to add or remove things often, the O(n) cost makes those operations impractical.</p><p>Unsorted arrays are only worth considering for very small collections where simplicity and cache speed up outweigh the slow lookup.</p><p>For the spell-checker problem, the hash table is the best approach. But something is worth considering: a hash table for 100,000 words still occupies a significant amount of memory. </p><p>What if we could trade a small amount of accuracy for a huge reduction in memory? What if we could have a data structure that is as fast as a hash table but uses only a fraction of the space, at the cost of occasionally saying a word is in the dictionary when it is not?</p><p>That is exactly what Bloom filters do, and we will explore them in a follow-up article.</p>]]></content:encoded></item><item><title><![CDATA[Why Distributed Systems Fail and How to Limit the Damage]]></title><description><![CDATA[A look at the most common failure modes and the techniques to reduce their impact.]]></description><link>https://newsletter.francofernando.com/p/why-distributed-systems-fail-and</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/why-distributed-systems-fail-and</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Sat, 04 Apr 2026 05:30:37 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!uJXx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f1391e2-e82d-49f3-af33-ab5be5edb902_645x395.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends,</p><p>Welcome to the 167th issue of the Polymathic Engineer.</p><p>A rule of thumb when working on a distributed system is to assume that anything can go wrong. The way engineers usually deal with this is by using scalability patterns like data partitioning, functional decomposition, and replication.</p><p>The problem with these patterns is that they all add more moving parts and make our systems more complicated. Since each part has a chance of failing, the more there are, the higher the chance that at least one of them will fail at any given moment. Software crashes, power outages, hardware faults, and memory leaks can happen all the time.</p><p>This isn't only a worry in theory. As we discussed in <a href="https://francofernando.substack.com/p/availability">another article</a>, a system can only be down for approximately 15 minutes a day to guarantee it is available 99.99% of the time. For three nines, that drops to 43 minutes per month. In general, the more nines you want, the faster your system needs to detect failures and recover from them when they occur. There is no time for a person to notice anything went wrong and fix it.</p><p>In this article, we will discuss the most common root causes of failures in distributed systems and see how to address them. The outline is as follows:</p><ul><li><p>Common root causes of failures</p></li><li><p>Managing risk</p></li><li><p>Redundancy</p></li><li><p>Correlation</p></li><li><p>Fault isolation</p></li><li><p>Shuffle Sharding and Cellular Architecture</p></li></ul><div><hr></div><p>Project-based learning is the best way to develop technical skills. <a href="https://app.codecrafters.io/join?via=FrancoFernando">CodeCrafters </a>is an excellent platform for tackling exciting projects, such as building your own Redis, Kafka, a <a href="https://app.codecrafters.io/join/dns-server?via=francofernando">DNS server</a>, SQLite, or Git from scratch. <a href="https://app.codecrafters.io/join?via=FrancoFernando">Sign up, and become a better software engineer</a>.</p><div><hr></div><h2>Common root causes of failures</h2><p>To build fault-tolerant distributed systems, you first need to fully understand what can go wrong. Let's start looking at the most common failure modes you can encounter.</p><h4>Infrastructure Faults</h4><p>Hardware doesn&#8217;t last forever. Hard drives fail, memory modules go bad, and network cards stop working. Power outages happen, and fiber cuts or electrical storms can take down whole data centers. The good news is that redundancy makes it straightforward to remedy these infrastructural problems. The underlying idea is simple: if one of your servers goes down, the others may take over.</p><h4>Incorrect Error Handling </h4><p>A <a href="https://scispace.com/pdf/an-analysis-of-production-failures-in-distributed-data-igrujmovmy.pdf">study of user-reported failures</a> from well-known distributed data storage showed an unexpected result. Most major failures were not caused by infrastructure issues. They were caused by mistakes made when dealing with non-fatal faults. The bugs were very easy to fix. In some cases, error handlers didn&#8217;t even look at errors; in others, handlers were only partially set up, or caught too generic exceptions. The main point was that most bugs could have been found with simple unit tests.</p><h4>Configuration Changes </h4><p>Changes to configurations cause failures more often than most engineers think, and it is not always a simple mistake like a typo in a database connection string that makes them risky. One example is a valid configuration change turning on a feature flag that hasn&#8217;t been used in years. Even if there is a code path, and the value looks correct, the behavior is no longer what was meant. </p><p>Configuration changes are challenging because they may not take effect right away. A change that is not valid might not be seen for hours or days if an application only gets a configuration value when a certain code path runs. This is why configuration should be managed like code: version-controlled, reviewed, and released incrementally.</p><h4>Single points of failure</h4><p>A single point of failure is any component that, brings the entire system down when it fails. The tricky part is that they are not always obvious. </p><p>People are a surprisingly common one. If someone has to manually execute a sequence of operational steps in the right order without making a mistake, it is only a matter of time before something goes wrong. Computers, on the other hand, are much better at executing instructions. This is why you should automate whenever possible.</p><p>But systems have non-human SPOFs as well. DNS is a good example: if clients can&#8217;t resolve your domain, it doesn&#8217;t matter how healthy your servers are. TLS certificates are another one: if yours expires, every client connection will be refused. </p><p>When designing a system, a useful exercise is to go through each component and ask what happens if it disappears. Some SPOFs can be removed with redundancy. Others can&#8217;t, but you can at least work to reduce how much damage they cause when they do fail.</p><h4>Network faults</h4><p>When two processes talk to each other over the network, a lot can go wrong. Messages might get lost, arrive late, or be sent more than once. The sender rarely knows which of these things happened because they only see a timeout. </p><p>But timeouts are the easy case. A more difficult situation is when the network doesn&#8217;t completely fail; it just gets slow. A process sends a request and gets a response, but it takes 10 times longer than usual. There is no error to catch and no timeout to trigger. The caller just waits, hanging on to resources while they do. These partial failures are sometimes called <em>gray failures</em> and are considerably harder to find than a clean crash. A slow dependency can silently bring the entire system to a halt before anybody knows something is wrong.</p><h4>Resource leaks</h4><p>Every process has a limited number of resources it can use, such as memory, threads, file handles, and database connections. When a bug causes one of these resources not to be released properly, you get a leak. The hard part is that leaks don&#8217;t show up right away. A slow memory leak might take days to fill up the heap. A connection pool that loses one connection per hour won&#8217;t cause problems until there are none left.</p><p>This is what makes resource leaks so dangerous: they pass all the tests in staging but don't show up in production until the app has been running long enough. And when they do ultimately fix the problem, it often doesn't seem like the original defect at all. A leaky connection doesn't make a sound, but it just shows up as a strange timeout in another part of the system.</p><h4>Load pressure</h4><p>A system can perform perfectly fine under normal conditions, but it can break down when the load gets too high. The hard aspect is that the extra load doesn't necessarily come from the users. Sometimes the spike comes from places you don&#8217;t expect: a web scraper hitting your API thousands of times per second, or a denial-of-service attack flooding your servers with traffic.</p><p>What makes load-related failures difficult is that they can turn a healthy system into an unhealthy one very quickly. When the thread pool is full, a service that responds in 50 milliseconds under normal load can take 5 seconds. And that slowdown does not stay in one place, but extends to all the other services that are waiting for a response.</p><h4>Cascading failures</h4><p>Cascading failures happen when a fault in a component causes faults in some others, which then trigger more faults, and so on. A common example is when a server gets too many requests, starts to answer slowly, and callers start to time out. Those callers retry, adding even more load to the already struggling server. The situation gets worse and worse until the whole system grinds to a halt.</p><p><em>Metastable failures</em> are the most dangerous kind of cascade failures. This happens when the system gets stuck in a bad state that doesn't go away even after the original cause has gone away. For example, a short spike in traffic creates a lot of retries. The retries themselves create enough load to produce more retries, and the system persists in this self-sustaining failure mode long after the original spike is over. When this happens, it is not possible to just wait for the problem to pass. The system needs an intervention to break the cycle.</p><h2>Managing Risk</h2><p>By now, the list of things that can go wrong may seem a bit too long. But just because a mistake could happen doesn't imply we have to fix it immediately.</p>
      <p>
          <a href="https://newsletter.francofernando.com/p/why-distributed-systems-fail-and">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[What Really Makes a Succesful Software Engineer]]></title><description><![CDATA[Five skills no course teach you, but matter more than your tech stack.]]></description><link>https://newsletter.francofernando.com/p/what-really-makes-a-succesful-software</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/what-really-makes-a-succesful-software</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Sat, 28 Mar 2026 11:31:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Gb7G!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff53db50e-3d1d-4dff-a350-87a2860212bb_1254x508.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Gb7G!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff53db50e-3d1d-4dff-a350-87a2860212bb_1254x508.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Gb7G!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff53db50e-3d1d-4dff-a350-87a2860212bb_1254x508.png 424w, https://substackcdn.com/image/fetch/$s_!Gb7G!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff53db50e-3d1d-4dff-a350-87a2860212bb_1254x508.png 848w, https://substackcdn.com/image/fetch/$s_!Gb7G!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff53db50e-3d1d-4dff-a350-87a2860212bb_1254x508.png 1272w, https://substackcdn.com/image/fetch/$s_!Gb7G!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff53db50e-3d1d-4dff-a350-87a2860212bb_1254x508.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Gb7G!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff53db50e-3d1d-4dff-a350-87a2860212bb_1254x508.png" width="1254" height="508" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f53db50e-3d1d-4dff-a350-87a2860212bb_1254x508.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:508,&quot;width&quot;:1254,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:332980,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/192201133?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff53db50e-3d1d-4dff-a350-87a2860212bb_1254x508.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Gb7G!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff53db50e-3d1d-4dff-a350-87a2860212bb_1254x508.png 424w, https://substackcdn.com/image/fetch/$s_!Gb7G!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff53db50e-3d1d-4dff-a350-87a2860212bb_1254x508.png 848w, https://substackcdn.com/image/fetch/$s_!Gb7G!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff53db50e-3d1d-4dff-a350-87a2860212bb_1254x508.png 1272w, https://substackcdn.com/image/fetch/$s_!Gb7G!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff53db50e-3d1d-4dff-a350-87a2860212bb_1254x508.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Hi Friends,</p><p>Welcome to the 166th issue of the Polymathic Engineer newsletter.</p><p>When you think about becoming a better software engineer, your mind probably goes straight to technical skills. Learning another framework or mastering a programming language, or practicing system design patterns. And yes, these things matter because they are what get you through the interview and into the job.</p><p>But here is the thing: technical skills are only half the equation. After almost 20 years in this industry, I have noticed that the engineers who truly excel share a set of skills that have little to do with code. Those are passive skills that work in the background, helping you through your daily routine without you even noticing. However, there are no online courses or bootcamps that teach them. You have to develop them yourself.</p><p>Any technical skill can be learned over time by reading documentation or watching a video. But the skills I am about to talk about are the cornerstones on which you grow as a developer. They are what help you through the learning process itself. If you skip them, it is like learning to run before you know how to walk.</p><p>The outline is as follows:</p><ul><li><p>Patience</p></li><li><p>Determination</p></li><li><p>An eternal student mindset</p></li><li><p>Accept criticism and learn from it</p></li><li><p>Know how to communicate</p></li></ul><div><hr></div><h4><strong><a href="https://coderabbit.link/tpe">Stop debating architecture in pull requests</a>.</strong></h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ci_p!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcfbd454-dc17-49e0-8f51-6eb5e6f4ee84_1246x652.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ci_p!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcfbd454-dc17-49e0-8f51-6eb5e6f4ee84_1246x652.png 424w, https://substackcdn.com/image/fetch/$s_!Ci_p!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcfbd454-dc17-49e0-8f51-6eb5e6f4ee84_1246x652.png 848w, https://substackcdn.com/image/fetch/$s_!Ci_p!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcfbd454-dc17-49e0-8f51-6eb5e6f4ee84_1246x652.png 1272w, https://substackcdn.com/image/fetch/$s_!Ci_p!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcfbd454-dc17-49e0-8f51-6eb5e6f4ee84_1246x652.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ci_p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcfbd454-dc17-49e0-8f51-6eb5e6f4ee84_1246x652.png" width="626" height="327.569823434992" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bcfbd454-dc17-49e0-8f51-6eb5e6f4ee84_1246x652.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:652,&quot;width&quot;:1246,&quot;resizeWidth&quot;:626,&quot;bytes&quot;:625529,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/192201133?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcfbd454-dc17-49e0-8f51-6eb5e6f4ee84_1246x652.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Ci_p!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcfbd454-dc17-49e0-8f51-6eb5e6f4ee84_1246x652.png 424w, https://substackcdn.com/image/fetch/$s_!Ci_p!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcfbd454-dc17-49e0-8f51-6eb5e6f4ee84_1246x652.png 848w, https://substackcdn.com/image/fetch/$s_!Ci_p!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcfbd454-dc17-49e0-8f51-6eb5e6f4ee84_1246x652.png 1272w, https://substackcdn.com/image/fetch/$s_!Ci_p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcfbd454-dc17-49e0-8f51-6eb5e6f4ee84_1246x652.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If your team&#8217;s pull requests are filled with comments like:</p><ul><li><p>This architecture won&#8217;t scale</p></li><li><p>We should approach this another way</p></li></ul><p>You are suffering from high costs and wasting developers&#8217; time.</p><p>Developers often realize what should have been built only after the PR discussion begins, leading to endless back-and-forth and complete rewrites.</p><p>Introducing the <a href="https://coderabbit.link/tpe">CodeRabbit Plan</a> that makes planning a first-class workflow before you write a single line of code. <a href="https://coderabbit.link/tpe">CodeRabbit Plan</a> lets teams design how a feature should be built before writing code, so PR reviews serve as validation rather than architecture debates.</p><ul><li><p><strong>Old Way:</strong> build &gt; PR &gt; debate</p></li><li><p><strong>New Way with Plan:</strong> plan &gt; align &gt; build</p></li></ul><p>Thanks to the CodeRabbit team for collaborating with me on this newsletter issue.</p><p><a href="https://coderabbit.link/tpe">Get Started Today</a></p><div><hr></div><h2>Patience</h2><p>Nothing says you are a software engineer like spending three hours debugging a piece of code, only to find the problem was a tiny detail forgotten somewhere in the middle.</p><p>If you think this is a sign of being a junior developer or of lacking experience, you are getting it wrong. I go through that same process every now and then, after almost 20 years in this profession. It never fully goes away.</p><p>The reality is that almost everything in software development requires time and effort. Understanding someone else&#8217;s code or researching how to solve a problem? Time and effort. Same for writing code and getting it actually to work.</p><p>Sure, you can take shortcuts. Copying and pasting code from an AI assistant feels like the quick path to a solution. But it only gets you halfway. The rest needs to come from you, and there is a lot of trial and error involved. You try something; it fails. Then you try something else, and it fails again. This is simply the process.</p><p>The engineers who burn out fastest are often the ones who expect everything to work on the first try. When it doesn&#8217;t, frustration builds up. Debugging for three hours isn&#8217;t so bad if you know that being patient is part of the job. It&#8217;s just another day at work.</p><h2>Determination</h2><p>Determination goes hand in hand with patience. Everyone who has done our job long enough knows it isn't easy, but setting the right expectations is the best way to avoid being discouraged when things go wrong.</p><p>The truth is, you will encounter roadblocks in your career. Problems that, once solved, turn into ten new ones. Bugs that take a long time to fix, like weeks or months. In any of these cases, quitting is a possibility.</p><p>There have been many occasions in my career when I wanted to quit programming. A lot of developers in our field would like to move to the middle of nowhere, away from technology, and become farmers. However, this is not a sign that something is wrong with software development. It only proves that our job can be frustrating at times.</p><p>That's why determination is so important. It is hard to know how determined you are until you face a struggle. But if you are already a determined person who doesn't give up after the first try, you will be a good developer.</p><p>The good news is that you can build determination over time. Every bug you fix, every roadblock you overcome, adds a little more to your reserves. You start to trust that you will figure it out eventually, because you have done it before.</p><h2>A Student Mindset</h2><p>Growth and learning are inseparable. You can't have one without the other. However, many developers forget this after they get a job or become more senior.</p><p>Our field never stops changing. Sometimes it moves forward with concepts that are really new. Other times, it goes back to previous concepts and presents them as the next big thing. Either way, if you don't pay attention, you will fall behind.</p><p>This doesn&#8217;t mean you need to chase every new framework or learn every trending language. That would be tiring and useless. What that means is that you should be open to the idea that what you know today won't always be relevant. The tools and practices you use now will change, and you will have to adjust to them.</p><p>I have seen developers who refuse to accept this. They learn one stack and think it will help them for the rest of their careers. It sometimes works for a few years. But eventually, the industry changes, and they are unable to move forward because they stopped learning too soon.</p><p>Technology drives technology. New advancements lead to new areas of research, new ways to work with data, and new ways of building products. When that happens, the tools we use have to adapt. If you adapt with them, you stay relevant. Otherwise, you get left behind. The key is not to learn everything. The key is to never close the door on learning.</p><h2>Accept Criticism and Use It to Learn</h2><p>Programming is not a solo activity. Even if you work as a freelancer, you will interact with other developers in one way or another. Giving and receiving feedback is part of that. </p><p>A good example is code reviews. It can be uncomfortable when other developers read your code to look for problems, missing standards, or flaws. However, if both sides do it well, code review can be one of the best methods to learn and grow.</p><p>The purpose of reviewing is to improve the code, and not to put down the person who wrote it. On the other side, if you are getting feedback, you need to keep in mind that it is not personal. The reviewers are sharing their experiences. Take their opinions to heart, and make sure you know why they were offered. You will learn a lot from this.</p><p>But feedback doesn't just show up during code reviews. You get feedback all the time, such as during performance reviews, in comments on open-source projects, or from a coworker pointing out an issue with your work. You should expect some feedback, but some will surprise you.</p><p>It is also crucial to make a distinction. Some feedback highlights a flaw in your work, like a bug you introduced. Other feedback is just negative noise: people venting their anger without giving any meaningful information. Learn how to tell them apart. Even a negative critique generally offers little insight. Don't worry about anything else; just focus on that.</p><p>If you only see feedback as something bad, you will start to doubt every decision you make. And that goes against the whole goal, which was to help you get better.</p><h2>Know How to Communicate</h2><p>This one is tough. Many developers, even experienced ones, struggle to communicate effectively with others. We spend so much of our time learning how to write logic for computers that we forget how to talk to people. Computers don't care about how well you speak or what words you use. They require clear, easy-to-understand instructions. But you also have to work with people, and people are all different.</p><p>When you need to ask a colleague for help, explain a problem you are facing, or help someone else with their issue, you have to switch from your machine syntax to your human syntax. This shift is not easy, but getting good at it gives you a big edge. If an interviewer sees that you can communicate well, you have already won half the battle.</p><p>How do you get better at this? One way is through writing. When I first started, I was bad at both writing and speaking. I spent hours reviewing emails that I believed were important, adding clarifications, and asking my peers to check that they made sense.</p><p>Things didn't change until I started making an effort to write online. Writing posts for my blog made me think of clearer ways to explain things. It didn't happen right away, but eventually something clicked. My oral communication also improved. I learnt how to talk to people better.</p><p>Communication is a skill you can practice right now, for free. Start a blog, write about what you are learning, or just explain a concept to a colleague. The more you do it, the better you get.</p><h2>Conclusion</h2><p>Getting the job is not the end of the line. It&#8217;s just the beginning. There are developers who think that once they get a job, they have made it. But it&#8217;s like thinking you&#8217;ve won a video game when you reach the highest level. In reality, you have simply unlocked a whole new set of challenges. Technical knowledge can get you through the door. But to get to the top, you need to be patient, determined, open to learning, good at talking to people, and able to take criticism. These are the building blocks for everything else you will learn along the way.</p>]]></content:encoded></item><item><title><![CDATA[Recursion in Practice: Iteration and Subproblems ]]></title><description><![CDATA[How to apply the first two recursive patterns to real problems.]]></description><link>https://newsletter.francofernando.com/p/recursion-in-practice-iteration-and</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/recursion-in-practice-iteration-and</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Sat, 21 Mar 2026 09:32:18 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!HcFg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e94954f-53d6-42d3-ace9-615351e76822_448x443.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends, </p><p>Welcome to the 165th issue of the Polymathic Engineer. This week, we keep going our recursion series with the second article.</p><p>In the <a href="https://newsletter.francofernando.com/p/mastering-recursion-the-foundations">previous article</a>, we covered the foundations: base cases, recursive steps, return strategies, tracing code, and complexity analysis. Now it&#8217;s time to put those basics to work.</p><p>This article covers the first two recursive patterns: Iteration and Subproblems. These are the most approachable patterns and a natural starting point before tackling all the others.</p><p>The outline is as follows:</p><ul><li><p>The Iteration pattern</p></li><li><p>Iterating forward and backward over arrays</p></li><li><p>Inserting at the bottom of a stack</p></li><li><p>Nested iteration with recursion</p></li><li><p>The Subproblems pattern</p></li><li><p>Towers of Hanoi</p></li><li><p>Checking if a string is a palindrome</p></li><li><p>The stair step problem</p></li></ul><div><hr></div><p>Project-based learning is the best way to develop technical skills. <a href="https://app.codecrafters.io/join?via=FrancoFernando">CodeCrafters </a>is an excellent platform for tackling exciting projects, such as building your own Redis, Kafka, a <a href="https://app.codecrafters.io/join/dns-server?via=francofernando">DNS server</a>, SQLite, or Git from scratch. <a href="https://app.codecrafters.io/join?via=FrancoFernando">Sign up, and become a better software engineer</a>.</p><div><hr></div><h2>The Iteration Pattern</h2><p>The iteration pattern is the simplest of the six. The basic idea is to replace a for loop with a recursive function. Each recursive call handles a step of the iteration, and the base case is when there are no more elements to process.</p><p>At first, this may seem like a waste of time. Why change a for loop that already works to recursion? There are a few times when it makes sense.</p><p>The first is when you need to access data in reverse order. Consider printing a linked list backward. With iteration, you would have to push all the values onto a stack and then pop them off. With recursion, the call stack does this for you automatically. The code becomes shorter and easier to follow.</p><p>The second is when the recursion takes care of memory for you. Some problems need temporary storage that matches the structure of the input. You don&#8217;t have to explicitly create and manage that storage yourself; instead, you can let the recursive calls hold the data in their stack frames. We will see a concrete example of this when we insert an element at the bottom of a stack.</p><p>The third is when you use functional programming. Languages like Haskell or certain styles of JavaScript avoid mutable state entirely. Recursion takes the place of loops in these contexts because there is no loop that doesn&#8217;t change a counter variable.</p><p>There is one thing to keep in mind: the iteration pattern doesn&#8217;t usually improve time complexity. Most of the time, the recursive version does the same amount of work as the iterative one. The advantage is that the code is cleaner, not that it runs faster. And since recursion uses stack frames, it can actually use more memory than a simple loop.</p><p>When you use this pattern, there are two questions to ask yourself. The first is: what is the iterative step? In a regular for loop, this might be i + 1, or i + 2, or something more complex. Your recursive call will mirror this. The second is: which way do you want to go? Going forward or backward? The answer affects where you put the recursive call relative to the work you do in each step. We'll see both directions in the next section.</p><h2>Iterating Forward and Backward</h2><p>Let&#8217;s start with a simple example: print every element in an array. Here is the iterative version (I know the code is not pythonic but it is better for the sake of the examples):</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;7ef6ba97-325a-49e9-9b78-2319af186948&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def print_array(arr):

    for i in range(len(arr)):

        print(arr[i])</code></pre></div><p>To make this code recursive, we need to think about what happens at each step of the iteration. In this case, we move from index i to index i + 1. The base case occurs when the index reaches the array's length, which means that there are no more elements to print. The recursive version does the same. It prints the current element, then moves to the next. When it runs out of elements, it stops.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;265093d2-80c9-4985-857c-d4c967026252&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def print_array(arr, i=0):
    if i == len(arr):
        return

    print(arr[i])
    print_array(arr, i + 1)</code></pre></div><p>Now here is something interesting. If you swap the order of the last two lines, you get the array printed in reverse:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;c975c189-af3d-4cb1-b11a-35b03ae2c1e6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def print_array_reverse(arr, i=0):
    if i == len(arr):
        return

    print_array_reverse(arr, i + 1)
    print(arr[i])</code></pre></div><p>The only change is that the print statement comes after the recursive call. This means we first go to the end of the array, then print the elements as the calls come back. The last element in the array is printed first, and so on.</p><p>This is a pattern you will see often: the order of the recursive call relative to the work determines the direction of processing. If you do the work first and then recurse, you process forward. If you recurse first and then do the work, you process backward.</p><p>You can use the same idea when you want to return a result rather than print it. As we discussed in the first article, you can either use a passed variable or build up the result as you return. Here is an example that sums all the elements in an array with the build-up approach:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;a1ac03a4-f360-4576-a2b6-e412338b21e9&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def sum_array(arr, i=0):
    if i == len(arr):
        return 0

    return arr[i] + sum_array(arr, i + 1)</code></pre></div><p>The function finds the sum from index i to the end. The base case says that the sum of an empty slice is 0. The recursive step states that the sum is the current element plus the sum of everything after it. As the calls return, the values add up.</p><p>If you prefer using a passed variable, the code looks like this:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;04eae0cd-1c94-479e-975a-a4f468ba8136&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def sum_array(arr, i=0, result=None):
    if result is None:
        result = [0]

    if i == len(arr):
        return result[0]

    result[0] += arr[i]
    return sum_array(arr, i + 1, result)</code></pre></div><p>Both approaches lead to the same answer. The build-up version is usually cleaner for simple cases, while the passed-variable can be better as the logic gets more complex.</p><h2>Inserting at the Bottom of a Stack</h2><p>Now, let&#8217;s look at a problem where recursion does more than just take the place of a for loop. You have to put something at the bottom of a stack without using any extra data structures.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HcFg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e94954f-53d6-42d3-ace9-615351e76822_448x443.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HcFg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e94954f-53d6-42d3-ace9-615351e76822_448x443.png 424w, https://substackcdn.com/image/fetch/$s_!HcFg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e94954f-53d6-42d3-ace9-615351e76822_448x443.png 848w, https://substackcdn.com/image/fetch/$s_!HcFg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e94954f-53d6-42d3-ace9-615351e76822_448x443.png 1272w, https://substackcdn.com/image/fetch/$s_!HcFg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e94954f-53d6-42d3-ace9-615351e76822_448x443.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HcFg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e94954f-53d6-42d3-ace9-615351e76822_448x443.png" width="244" height="241.27678571428572" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9e94954f-53d6-42d3-ace9-615351e76822_448x443.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:443,&quot;width&quot;:448,&quot;resizeWidth&quot;:244,&quot;bytes&quot;:134067,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/189814178?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e94954f-53d6-42d3-ace9-615351e76822_448x443.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HcFg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e94954f-53d6-42d3-ace9-615351e76822_448x443.png 424w, https://substackcdn.com/image/fetch/$s_!HcFg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e94954f-53d6-42d3-ace9-615351e76822_448x443.png 848w, https://substackcdn.com/image/fetch/$s_!HcFg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e94954f-53d6-42d3-ace9-615351e76822_448x443.png 1272w, https://substackcdn.com/image/fetch/$s_!HcFg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e94954f-53d6-42d3-ace9-615351e76822_448x443.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If you try to do this iteratively, you run into a problem. You can only access the top of a stack, so you have to pop everything off, insert your item, and push everything back on. This requires a temporary stack to hold the elements (In Python, we use a list as a stack, with append() to push and pop() to remove from the top):</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;86348a9e-417a-4d32-ad2c-7ebf2d67b8f0&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def insert_at_bottom_iterative(stack, item):
    temp = []
    while stack:
        temp.append(stack.pop())

    stack.append(item)
    while temp:
        stack.append(temp.pop())</code></pre></div><p>The code works, but you need to create and maintain the temporary stack. When you use recursion, the call stack takes care of this implicitly. The recursive approach pops elements off one at a time, makes a recursive call, and then pushes each element back on after the call comes back. The base case is when the stack is empty, at which point we insert the item. As the recursive calls return, each previously popped element gets pushed back on top:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;f8aeb9db-74ff-4f60-b17d-369335e05f30&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def insert_at_bottom(stack, item):
    if not stack:
        stack.append(item)
        return

    top = stack.pop()
    insert_at_bottom(stack, item)
    stack.append(top)</code></pre></div><p>Let&#8217;s trace through this with a concrete example. Suppose the stack is [1, 2, 3, 4] (with 4 on top) and we want to insert 5 at the bottom.</p><p>The first call pops 4 and saves it. The stack is now [1, 2, 3]. The second call pops 3. The stack is [1, 2]. The third call pops 2. The stack is [1]. The fourth call pops 1, leaving the stack empty.</p><p>Now we hit the base case. We append 5, so the stack is [5]. Then the calls start coming back. The fourth call appends 1, giving us [5, 1]. The third call appends 2, giving us [5, 1, 2]. The second call appends 3, giving us [5, 1, 2, 3]. The first call appends 4, and we end up with [5, 1, 2, 3, 4].</p><p>The time complexity is O(n) because we need to pop and push each element once. The space complexity is also O(n) since the call stack must hold all popped elements. This is the same as the iterative version, but the recursive code is shorter and easier to read.</p><h2>Nested Iteration with Recursion</h2>
      <p>
          <a href="https://newsletter.francofernando.com/p/recursion-in-practice-iteration-and">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Mastering Recursion: the Foundations]]></title><description><![CDATA[Understanding when to use recursion, how to structure recursive functions, and how to go through any recursive code.]]></description><link>https://newsletter.francofernando.com/p/mastering-recursion-the-foundations</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/mastering-recursion-the-foundations</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Sat, 14 Mar 2026 07:31:04 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!BbZc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faefe698d-ed70-48d8-ae3f-e6884e0f6f92_832x379.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends,</p><p>Welcome to the 164th issue of the Polymathic Engineer newsletter.</p><p>Recursion is one of those topics that many software engineers &#8220;understand&#8221; in theory but struggle with in practice. They know the textbook definition (a function that calls itself). They have seen the Fibonacci example multiple times. But when they are asked to solve a recursive problem in an interview, something doesn&#8217;t work.</p><p>The problem is not that they are not smart, but that they don&#8217;t have the correct mental models. It took me a while to figure this out. When someone explained it to me, I was able to follow recursive code, but writing it from scratch often felt like guessing. The turning point was when I stopped thinking of recursion as a single, isolated technique and began seeing it as a set of patterns, each with its own structure.</p><p>In this article, we will start building the basic mental models you need to actually use recursion. In future articles, we will apply these foundations to specific patterns and problems.</p><p>The outline is as follows:</p><ul><li><p>When to use recursion</p></li><li><p>The two building blocks: base case and recursive step</p></li><li><p>Three ways to return results from recursive functions</p></li><li><p>How to trace any recursive code (even when you&#8217;re lost)</p></li><li><p>Time and space complexity for recursion</p></li><li><p>A preview of the six recursive patterns</p></li></ul><div><hr></div><p>Project-based learning is the best way to develop technical skills. <a href="https://app.codecrafters.io/join?via=FrancoFernando">CodeCrafters </a>is an excellent platform for tackling exciting projects, such as building your own Redis, Kafka, a <a href="https://app.codecrafters.io/join/dns-server?via=francofernando">DNS server</a>, SQLite, or Git from scratch. <a href="https://app.codecrafters.io/join?via=FrancoFernando">Sign up, and become a better software engineer</a>.</p><div><hr></div><h2>When to Use Recursion</h2><p>There is a fact about recursion that people often overlook: any problem that can be solved recursively can also be solved iteratively. There is no case where you need to use recursion.</p><p>So the right question isn&#8217;t &#8220;can I use recursion here?&#8221; but rather &#8220;is recursion easier here?&#8221; There are a few questions you can ask yourself to figure this out.</p><ol><li><p><strong>Does the problem break down into subproblems? </strong>This is mostly what recursion is about. If a problem breaks into smaller versions of itself, recursion is probably a good fit. For example, sorting an array can be decomposed into sorting two halves and merging them. Finding a path through a maze can be broken into taking one step and finding a path from there.</p></li><li><p><strong>Could you solve the problem using an arbitrary number of for loops? </strong>This point gets people confused. Let&#8217;s say you are asked to print all possible 6-digit numbers. It sounds easy: you can just write six nested for loops, one for each digit. But what if you are told to print all possible n-digit numbers? You can't write n nested for loops anymore since the number of loops changes depending on the input. This is an excellent example of when to use recursion, because it effectively allows you to create arbitrary nesting.</p></li><li><p><strong>Does the problem fit one of the common recursive patterns? </strong>For most recursive problems you see in interviews, there are six patterns that you can look for. We&#8217;ll preview them at the end of this article and cover them in depth in future ones. If you recognize that a problem fits one of these patterns, that is a strong hint to use recursion.</p></li><li><p><strong>Is it easier to solve recursively than iteratively? </strong>Sometimes the recursive code is just cleaner. Consider the task of printing a linked list in reverse order. Without recursion, you would have to push all the values onto a stack, then pop and print them. Recursively, the process reduces to three lines of code:</p></li></ol><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;9f4d3456-0fdc-4f38-94e8-8cac1b7e8a5c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def print_reverse(node):

  if node is None:
    return

  print_reverse(node.next)
  print(node.value)</code></pre></div><p>The recursive version uses the call stack as your stack, so you don&#8217;t need to explicitly manage one yourself. The code is more concise and probably easier to grasp.</p><p>However, there is a trade-off. If the time complexity is the same, the iterative solution usually runs faster. Function calls, stack frames, and other things that recursion does all add up. In general, if you have the possibility to choose between two equally simple recursive and iterative solutions, the iterative one will perform better. </p><p>But in an interview, clarity often matters more than micro-optimizations, so it is wise to pick whatever approach lets you write correct, readable code faster.</p><h2>Base Case and Recursive Step</h2><p>Every recursive function has two essential building blocks: a base case and a recursive step. Getting these right is what separates working code from infinite loops and stack overflows.</p><p>The base case is the point at which the recursion stops. It is the simplest form of the problem, and it can be solved immediately without any further recursive calls.</p><p>A useful technique for identifying the base case is to start with small examples. What is the answer when the input size is zero or one? These edge cases are usually the base cases.</p><p>For example, if you are writing a function to compute the sum of an array of integers, an empty array has a sum of 0, and an array with one element has a sum equal to that element. Both can be solved without recursion, making them natural base cases.</p><p>There is one important rule to follow here: the base case must match the return type of your function. If your function returns a list, your base case must return a list (not None or <code>0</code>). This sounds obvious, but it is a common source of bugs. People often get stuck on this because they try to guess what the base case is instead of using concrete examples to figure it out.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;3880c540-d3df-49f9-8865-254ac40bd018&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># Wrong: function returns a list, but base case returns None
def get_combinations(items):
    if len(items) == 0:
        return None  # This will break the calling code
    ...

# Correct: base case returns an empty list
def get_combinations(items):
    if len(items) == 0:
        return [[]]  # A list containing the empty combination
    ...</code></pre></div><p>In the recursive step,  you divide the problem into smaller subproblems and combine the results. There are two things to keep in mind here.</p><p>The first is that a subproblem should have the same shape as the original problem. If your function computes the sum of elements of an array from index i to the end, then your recursive call should compute the sum of elements from index i+1 to the end. The meaning is the same, just applied to a smaller input.</p><p>The second critical thing is that the recursive step must converge to the base case. If your base case is i == 0 and your recursive call passes i + 1, you are moving away from the base case, not toward it. This causes an infinite recursion and eventually a stack overflow.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;8d8de827-f213-4e0a-82a7-fe79f180cff1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># Wrong: moves away from base case
def countdown(n):
    if n == 0:
        return
    countdown(n + 1)  # n grows, never reaches 0

# Correct: moves toward base case
def countdown(n):
    if n == 0:
        return
    countdown(n - 1)  # n shrinks toward 0</code></pre></div><p>When you are writing code in an interview, you don&#8217;t get to run your code and see the stack overflow error. You need to find this mistake by looking at the code you wrote. A quick check that your recursive step gets you closer to the base case can save you from an embarrassing bug.</p><h2>Three Ways to Return Results</h2><p>When you write a recursive function, you need to decide how to collect and return the results. There are three main ways you can do this: use a global variable, use a passed variable, or build up results as you return. Each has its trade-offs.</p><p>The simplest approach is to use a global variable. You set a variable outside the code and change it as the recursion proceeds. As an example, consider the following code snippet that counts all the even numbers in an array:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;1ea670e3-d346-4aa4-8a4b-d4d9abf59e02&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def count_evens(arr, i):
    global result
    if i == len(arr):
        return
    if arr[i] % 2 == 0:
        result += 1
    count_evens(arr, i + 1)</code></pre></div><p>This works, but global variables are generally not a good idea. They make code harder to test and reason about. A better alternative is to use a passed variable.</p>
      <p>
          <a href="https://newsletter.francofernando.com/p/mastering-recursion-the-foundations">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[The Building Blocks of Concurrent Programming]]></title><description><![CDATA[Understanding processes, threads, and how they communicate.]]></description><link>https://newsletter.francofernando.com/p/the-building-blocks-of-concurrent</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/the-building-blocks-of-concurrent</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Fri, 06 Mar 2026 13:13:16 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!O5o3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1633e3-f246-461c-a881-28cf61faacb2_395x374.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends,</p><p>Welcome to the 163rd issue of the Polymathic Engineer newsletter.</p><p>Concurrent programming involves breaking down applications into separate units of work. These units, known as <em>tasks,</em> set up the execution flow of an application. At their core, tasks are just abstractions. At some point, they must eventually be mapped to the physical devices that execute the code.</p><p>Thank goodness, we don&#8217;t have to worry about this. The operating system, which is another layer of abstraction, handles this for us. Its role is to make the best use of the available hardware as possible, but it is not a magical box. Still, programmers need to organize their work in a way that helps the operating system use hardware in the best way possible.</p><p>In this article, we will discuss processes and threads, which are the two main building blocks of concurrency, and understand how they can talk to each other (Demo code in this <a href="https://github.com/FrancoFernando/system-design-mastery/tree/9d66439a032232a9808274109f6a007317c9796b/low-level/concurrency-building-blocks">GitHub repo</a>).</p><p>The outline is as follows:</p><ul><li><p>Processes</p></li><li><p>Threads</p></li><li><p>Processes vs Threads</p></li><li><p>Interprocess Communication</p></li><li><p>Message-Passing Mechanisms</p></li><li><p>The Thread Pool Pattern</p></li></ul><div><hr></div><p>Project-based learning is the best way to develop technical skills. <a href="https://app.codecrafters.io/join?via=FrancoFernando">CodeCrafters </a>is an excellent platform for tackling exciting projects, such as building your own Redis, Kafka, a <a href="https://app.codecrafters.io/join/dns-server?via=francofernando">DNS server</a>, SQLite, or Git from scratch. <a href="https://app.codecrafters.io/join?via=FrancoFernando">Sign up, and become a better software engineer</a>.</p><div><hr></div><h2>Processes</h2><p>The informal definition of a process is simple: it is a running program. A program by itself is just a file sitting on a disk. It contains a set of instructions, but it does nothing on its own. When the operating system takes those instructions and starts executing them on hardware, it becomes a process.</p><p>Think of it like a recipe in a cookbook. The recipe itself is just words on paper listing ingredients, steps, and timings. It doesn&#8217;t make anything by itself. But if a chef reads it and starts cooking, it becomes a meal in progress. Source code works the same way.</p><p>It is just a passive sequence of instructions. Developers write code using abstractions like memory, files, and network connections, but the actual resources must be given at runtime. The OS wraps all of this into what we call a process.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!O5o3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1633e3-f246-461c-a881-28cf61faacb2_395x374.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!O5o3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1633e3-f246-461c-a881-28cf61faacb2_395x374.png 424w, https://substackcdn.com/image/fetch/$s_!O5o3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1633e3-f246-461c-a881-28cf61faacb2_395x374.png 848w, https://substackcdn.com/image/fetch/$s_!O5o3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1633e3-f246-461c-a881-28cf61faacb2_395x374.png 1272w, https://substackcdn.com/image/fetch/$s_!O5o3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1633e3-f246-461c-a881-28cf61faacb2_395x374.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!O5o3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1633e3-f246-461c-a881-28cf61faacb2_395x374.png" width="395" height="374" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0d1633e3-f246-461c-a881-28cf61faacb2_395x374.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:374,&quot;width&quot;:395,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:140300,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/189250784?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1633e3-f246-461c-a881-28cf61faacb2_395x374.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!O5o3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1633e3-f246-461c-a881-28cf61faacb2_395x374.png 424w, https://substackcdn.com/image/fetch/$s_!O5o3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1633e3-f246-461c-a881-28cf61faacb2_395x374.png 848w, https://substackcdn.com/image/fetch/$s_!O5o3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1633e3-f246-461c-a881-28cf61faacb2_395x374.png 1272w, https://substackcdn.com/image/fetch/$s_!O5o3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1633e3-f246-461c-a881-28cf61faacb2_395x374.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A process is made of several parts that the OS keeps track of:</p><ul><li><p><strong>Address space</strong>: the memory the process can see and access</p></li><li><p><strong>Executable</strong>: the file with the machine instructions</p></li><li><p><strong>Process ID (PID)</strong>: a unique name to identify the process</p></li><li><p><strong>Process state</strong>: whether it is running, waiting, or finished</p></li><li><p><strong>Open files and connections</strong>: any resources the process is using</p></li></ul><p>All of these together make up the <strong>execution context</strong>. Since so many things are packed into a process, starting a new one is a pretty heavy thing to do, and processes are often called <em>heavyweight</em>.</p><p>If you look at a process as a whole, its lifecycle is, its lifecycle is straightforward. First, it doesn&#8217;t exist. After that, the OS creates it and places it in memory (the <em>Created</em> state). From there, it moves to the <em>Ready</em> state: it can run at any moment, but the CPU hasn&#8217;t picked it up yet. When the OS scheduler selects it, the process goes into the <em>Running</em> state. Once it completes or fails, it reaches the <em>Terminated</em> state.</p><p>Creating and terminating a process is relatively expensive since so many resources are attached to it and must be assigned or released.</p><p>Processes can create their own processes, called <em>child processes</em>, using system calls like fork() or spawn(). This is called <em>spawning</em>. Each child process gets its own independent memory address space, which means it is completely isolated from the parent process and any other children.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6vka!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a81d2b6-a2d8-4631-b725-4bed034b70ef_1051x143.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6vka!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a81d2b6-a2d8-4631-b725-4bed034b70ef_1051x143.png 424w, https://substackcdn.com/image/fetch/$s_!6vka!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a81d2b6-a2d8-4631-b725-4bed034b70ef_1051x143.png 848w, https://substackcdn.com/image/fetch/$s_!6vka!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a81d2b6-a2d8-4631-b725-4bed034b70ef_1051x143.png 1272w, https://substackcdn.com/image/fetch/$s_!6vka!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a81d2b6-a2d8-4631-b725-4bed034b70ef_1051x143.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6vka!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a81d2b6-a2d8-4631-b725-4bed034b70ef_1051x143.png" width="1051" height="143" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8a81d2b6-a2d8-4631-b725-4bed034b70ef_1051x143.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:143,&quot;width&quot;:1051,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:96782,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/189250784?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a81d2b6-a2d8-4631-b725-4bed034b70ef_1051x143.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!6vka!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a81d2b6-a2d8-4631-b725-4bed034b70ef_1051x143.png 424w, https://substackcdn.com/image/fetch/$s_!6vka!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a81d2b6-a2d8-4631-b725-4bed034b70ef_1051x143.png 848w, https://substackcdn.com/image/fetch/$s_!6vka!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a81d2b6-a2d8-4631-b725-4bed034b70ef_1051x143.png 1272w, https://substackcdn.com/image/fetch/$s_!6vka!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a81d2b6-a2d8-4631-b725-4bed034b70ef_1051x143.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>This is where things get interesting for concurrency. By spawning multiple processes, we can split execution across them and run them simultaneously on parallel hardware.</p><p>However, the isolation is both a good and a bad thing. Since two processes don&#8217;t share much by default, communication between them requires special mechanisms, which are usually several orders of magnitude slower than direct memory access. We&#8217;ll talk about that more later in this article.</p><h2>Threads</h2><p>Most operating systems let you share memory between processes, but this takes extra effort. However, there is a different abstraction that lets you share memory and other kinds of resources in a simpler way: threads.</p><p>At the end of the day, a program is simply a list of machine instructions that must be carried out in the right order. The OS uses the concept of a thread to do this. A thread is an independent stream of instructions whose execution can be scheduled by the OS.</p><p>In the previous section, we said that a process is a running program plus resources. If we break that apart, a process is a container of resources (address space, files, network connections, and so on), while a thread is the dynamic part: the set of instructions that run inside that container. From the OS&#8217;s point of view, a process can be seen as a unit of resources, while a thread can be seen as a unit of execution.</p><p>The idea behind threads is that sharing a common address space is the most efficient way for processes to communicate. Threads in the same process can share resources (address space, files, connections, and data) with each other and their parent process, in an easy way.</p><p>Each thread also keeps its own state to allow for safe, local, independent execution of its instructions. Each thread is unaware of the other threads unless it is trying to mess with them on purpose. The OS manages threads and distributes them across available processor cores. This makes a multithreaded program an effective way to run multiple tasks concurrently.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!f5Me!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb14302e-4a13-484d-bfdb-ff9cdc44abe0_775x448.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!f5Me!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb14302e-4a13-484d-bfdb-ff9cdc44abe0_775x448.png 424w, https://substackcdn.com/image/fetch/$s_!f5Me!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb14302e-4a13-484d-bfdb-ff9cdc44abe0_775x448.png 848w, https://substackcdn.com/image/fetch/$s_!f5Me!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb14302e-4a13-484d-bfdb-ff9cdc44abe0_775x448.png 1272w, https://substackcdn.com/image/fetch/$s_!f5Me!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb14302e-4a13-484d-bfdb-ff9cdc44abe0_775x448.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!f5Me!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb14302e-4a13-484d-bfdb-ff9cdc44abe0_775x448.png" width="577" height="333.54322580645163" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cb14302e-4a13-484d-bfdb-ff9cdc44abe0_775x448.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:448,&quot;width&quot;:775,&quot;resizeWidth&quot;:577,&quot;bytes&quot;:412035,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/189250784?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb14302e-4a13-484d-bfdb-ff9cdc44abe0_775x448.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!f5Me!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb14302e-4a13-484d-bfdb-ff9cdc44abe0_775x448.png 424w, https://substackcdn.com/image/fetch/$s_!f5Me!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb14302e-4a13-484d-bfdb-ff9cdc44abe0_775x448.png 848w, https://substackcdn.com/image/fetch/$s_!f5Me!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb14302e-4a13-484d-bfdb-ff9cdc44abe0_775x448.png 1272w, https://substackcdn.com/image/fetch/$s_!f5Me!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb14302e-4a13-484d-bfdb-ff9cdc44abe0_775x448.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Historically, hardware vendors implemented their own versions of threads, and these implementations were quite different. This made it challenging for programmers to write portable multithreaded applications. For UNIX systems, IEEE POSIX defined a standard programming interface. Implementations that follow this standard are called POSIX threads or Pthreads.</p><p>Every program we run causes the OS to create a process, and every process has at least one thread. A process without a thread cannot exist. When we start a program, a main execution thread is created. Any thread, even the main one, can create child threads at any time.</p><p>Threads have much less memory overhead than the standard <code>fork()</code> function. Since threads use the same process, nothing is copied. This is one reason why threads are sometimes called &#8220;lightweight processes.&#8221; It takes less time for the OS to assign and handle thread resources, plus starting and ending threads is faster than starting and ending processes.</p><p>However, there is a catch. The OS provides complete independence of processes from each other, so if one of them crashes, other processes are not harmed. This is not true for threads. All threads in a process use the same shared resources, so if one crashes or messes up something, it&#8217;s likely that the others will too. To avoid this, programmers need to make sure that shared resources are only used by one thread at a time and give them more control over how threads behave.</p><h2>Processes vs Threads</h2><p>To illustrate the difference between processes and threads, let&#8217;s go back to our recipe example. Imagine you run a restaurant and need to prepare three different dishes.</p>
      <p>
          <a href="https://newsletter.francofernando.com/p/the-building-blocks-of-concurrent">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Getting Started with OpenCV: A Practical Introduction]]></title><description><![CDATA[Learn how images work under the hood and build your first computer vision application.]]></description><link>https://newsletter.francofernando.com/p/getting-started-with-opencv-a-practical</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/getting-started-with-opencv-a-practical</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Fri, 27 Feb 2026 10:14:09 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!GcAE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0da3ca-92a3-47d9-b436-e1bf8737c951_1002x382.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends,</p><p>Welcome to the 162nd issue of the Polymathic Engineer newsletter. </p><p>As everybody knows, computer vision is everywhere. From the filters on your phone camera to self-driving cars, from medical imaging to security systems, the ability for machines to &#8220;see&#8221; and understand visual information has become integral to modern software.</p><p>If you want to get started with computer vision, OpenCV is the tool you should look at. This open-source library has been around since 2000, and it is used by everyone from hobbyists to large companies like Google and Intel. You can build many cool applications with it, such as face detection, object tracking, and augmented reality. But before you can do anything interesting, you need to understand the basics: how images are represented in memory, how to read and write them, and how to display results to users.</p><p>In this article, we will build that basic know-how. We won&#8217;t just look at the code, but we will also explain why things work the way they do. If you get a solid understanding of these fundamentals, it will be much easier to grasp everything else you learn about computer vision.</p><p>The outline is as follows:</p><ul><li><p>How Images Are Represented in Memory</p></li><li><p>Reading and Writing Images</p></li><li><p>Working with Video and Camera Input</p></li><li><p>Displaying Results and Handling User Input</p></li></ul><div><hr></div><p>Project-based learning is the best way to develop technical skills. <a href="https://app.codecrafters.io/join?via=FrancoFernando">CodeCrafters </a>is an excellent platform for tackling exciting projects, such as building your own Redis, Kafka, a <a href="https://app.codecrafters.io/join/dns-server?via=francofernando">DNS server</a>, SQLite, or Git from scratch. <a href="https://app.codecrafters.io/join?via=FrancoFernando">Sign up, and become a better software engineer</a>.</p><div><hr></div><h2>How Images Are Represented in Memory</h2><p>To work with images in OpenCV, you first need to understand what an image actually is at the code level. An image is a NumPy array: a grid of numbers where each number represents a pixel value.</p><p>Let&#8217;s begin with the simplest case: a grayscale image. Each pixel is a number between 0 and 255, where 0 is black and 255 is white. Any other number in between is a shade of gray. This is how you can make a 3x3 black picture from scratch:</p><pre><code>import numpy as np
img = np.zeros((3, 3), dtype=np.uint8)
print(img)
"""Output: 
[[0 0 0]
[0 0 0]
[0 0 0]] 
"""</code></pre><p>Color images work the same way. However, each pixel has three values instead of one, corresponding to the three color channels. One thing that confuses many newbies is that OpenCV uses the BGR (Blue, Green, Red) format, not RGB. When you change our previous picture to color, the NumPy array gets a third dimension:</p><pre><code>import cv2
img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
print(img.shape) # Output: (3, 3, 3)</code></pre><p>Now each pixel is a list of three values: [Blue, Green, Red]. A pure blue pixel would be [255, 0, 0], not [0, 0, 255] as you might expect from RGB. The middle image below has four colored quadrants: the blue quadrant (top-left corner) is created with [255, 0, 0], the green quadrant (top-right) is created with [0, 255, 0], the red quadrant (bottom-left) is created with [0, 0, 255], and the white quadrant is created with [255, 255, 255].</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GcAE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0da3ca-92a3-47d9-b436-e1bf8737c951_1002x382.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GcAE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0da3ca-92a3-47d9-b436-e1bf8737c951_1002x382.png 424w, https://substackcdn.com/image/fetch/$s_!GcAE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0da3ca-92a3-47d9-b436-e1bf8737c951_1002x382.png 848w, https://substackcdn.com/image/fetch/$s_!GcAE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0da3ca-92a3-47d9-b436-e1bf8737c951_1002x382.png 1272w, https://substackcdn.com/image/fetch/$s_!GcAE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0da3ca-92a3-47d9-b436-e1bf8737c951_1002x382.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GcAE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0da3ca-92a3-47d9-b436-e1bf8737c951_1002x382.png" width="1002" height="382" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6b0da3ca-92a3-47d9-b436-e1bf8737c951_1002x382.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:382,&quot;width&quot;:1002,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:276381,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/187102634?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0da3ca-92a3-47d9-b436-e1bf8737c951_1002x382.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!GcAE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0da3ca-92a3-47d9-b436-e1bf8737c951_1002x382.png 424w, https://substackcdn.com/image/fetch/$s_!GcAE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0da3ca-92a3-47d9-b436-e1bf8737c951_1002x382.png 848w, https://substackcdn.com/image/fetch/$s_!GcAE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0da3ca-92a3-47d9-b436-e1bf8737c951_1002x382.png 1272w, https://substackcdn.com/image/fetch/$s_!GcAE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0da3ca-92a3-47d9-b436-e1bf8737c951_1002x382.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>You can inspect any image&#8217;s structure using three properties:</p><ul><li><p><strong>shape</strong>: Returns the dimensions. For a color image, this is (height, width, channels). For grayscale, it&#8217;s just (height, width).</p></li><li><p><strong>size</strong>: The total number of elements in the array. For a 100&#215;100 BGR image, this would be 30,000 (100&#215;100&#215;3).</p></li><li><p><strong>dtype</strong>: The data type of the pixels. Most images use uint8 (unsigned 8-bit integers), which gives you the 0-255 range.</p></li></ul><p>Getting familiar with this layout is critical to do everything else in OpenCV. When you manipulate images, you are really just doing math on NumPy arrays.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.francofernando.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">The Polymathic Engineer is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Reading and Writing Images</h2><p>Now that you know how images are stored in memory, let&#8217;s have a look at how to load them from files and save them back. To do this, OpenCV has two key methods: imread and imwrite. The first loads an image, while the second saves an image. Their basic usage is straightforward:</p><pre><code>import cv2
img = cv2.imread(&#8217;photo.png&#8217;)
cv2.imwrite(&#8217;photo.jpg&#8217;, img)</code></pre><p>Even if the original file is grayscale, imread loads images by default in the BGR color format. If you want to load an image as grayscale, you need to specify it:</p><pre><code>gray_img = cv2.imread(&#8217;photo.png&#8217;, cv2.IMREAD_GRAYSCALE)</code></pre><p>Using the cv2<code>.</code>IMREAD_UNCHANGED option, you can also load the image as it was saved, including the alpha (transparency) channel if it exists. </p><p>The cool thing is that once you have an image loaded, you can modify it directly since it is just a NumPy array. For example, if you want to change single pixels to white, you can just assign new values:</p><pre><code>img[0, 0] = [255, 255, 255] # Top-left pixel is now white</code></pre><p>For bulk changes, NumPy&#8217;s array slicing is much faster than looping through pixels one by one. For example, this is how you can remove all green from an image or copy regions of an image using slicing:</p><pre><code>img[:, :, 1] = 0 # Set all green channel values to zero

# Copy a 100x100 region from the top-left corner
roi = img[0:100, 0:100]

# Paste it somewhere else
img[200:300, 200:300] = roi</code></pre><p>Of course, when copying regions, you need to ensure the areas are the same size. If the shapes don't match, NumPy will raise an error. The following image shows the result of manipulating the four-quadrant image we saw in the previous section: the green channel is removed from the top half, the bottom-left is set to blue, and a region is copied from the bottom-right to the top-left.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9vy3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ee4b2db-7ee5-467a-b146-4295d4f29cac_712x356.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9vy3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ee4b2db-7ee5-467a-b146-4295d4f29cac_712x356.png 424w, https://substackcdn.com/image/fetch/$s_!9vy3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ee4b2db-7ee5-467a-b146-4295d4f29cac_712x356.png 848w, https://substackcdn.com/image/fetch/$s_!9vy3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ee4b2db-7ee5-467a-b146-4295d4f29cac_712x356.png 1272w, https://substackcdn.com/image/fetch/$s_!9vy3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ee4b2db-7ee5-467a-b146-4295d4f29cac_712x356.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9vy3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ee4b2db-7ee5-467a-b146-4295d4f29cac_712x356.png" width="410" height="205" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9ee4b2db-7ee5-467a-b146-4295d4f29cac_712x356.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:356,&quot;width&quot;:712,&quot;resizeWidth&quot;:410,&quot;bytes&quot;:31117,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/187102634?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ee4b2db-7ee5-467a-b146-4295d4f29cac_712x356.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!9vy3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ee4b2db-7ee5-467a-b146-4295d4f29cac_712x356.png 424w, https://substackcdn.com/image/fetch/$s_!9vy3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ee4b2db-7ee5-467a-b146-4295d4f29cac_712x356.png 848w, https://substackcdn.com/image/fetch/$s_!9vy3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ee4b2db-7ee5-467a-b146-4295d4f29cac_712x356.png 1272w, https://substackcdn.com/image/fetch/$s_!9vy3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ee4b2db-7ee5-467a-b146-4295d4f29cac_712x356.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>Working with Video and Camera Input</h2><p>A video is simply a sequence of images, and OpenCV processes them the same way it processes images: frame by frame. The VideoCapture class lets you read videos, and the VideoWriter lets you save them.</p><p>The following code snippet creates a VideoCapture object and then loops through the frames to read from a video file:</p><pre><code>video = cv2.VideoCapture('input.avi')

while True:
    success, frame = video.read()
    if not success:
        break
    # Do something with the frame</code></pre><p>The read method returns two values: a boolean flag indicating whether a frame was successfully read, and the frame itself. When the video ends, success is set to False, and the loop is exited. </p><p>The amazing part is that using a webcam to capture works almost the same way. You just pass a device index instead of a filename. The default camera's index is usually 0. To save a video, you need a VideoWriter object. This requires a few more parameters: the output filename, a codec, the frame rate, and the frame size:</p><pre><code>import cv2

camera = cv2.VideoCapture(0)
fps = 30
width = int(camera.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(camera.get(cv2.CAP_PROP_FRAME_HEIGHT))

writer = cv2.VideoWriter(
    'output.avi',
    cv2.VideoWriter_fourcc('X', 'V', 'I', 'D'),
    fps,
    (width, height)
)

while True:
    success, frame = camera.read()
    if not success:
        break
    writer.write(frame)</code></pre><p>The codec determines how the video is compressed. Common options include XVID for AVI files and MP4V for MP4 files. Of course, the available codecs depend on your system, so you might need to experiment to find one that works.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GQeF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F461e32c1-3b75-4957-aa65-21bde2feab7b_836x198.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GQeF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F461e32c1-3b75-4957-aa65-21bde2feab7b_836x198.png 424w, https://substackcdn.com/image/fetch/$s_!GQeF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F461e32c1-3b75-4957-aa65-21bde2feab7b_836x198.png 848w, https://substackcdn.com/image/fetch/$s_!GQeF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F461e32c1-3b75-4957-aa65-21bde2feab7b_836x198.png 1272w, https://substackcdn.com/image/fetch/$s_!GQeF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F461e32c1-3b75-4957-aa65-21bde2feab7b_836x198.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GQeF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F461e32c1-3b75-4957-aa65-21bde2feab7b_836x198.png" width="727" height="172.18421052631578" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/461e32c1-3b75-4957-aa65-21bde2feab7b_836x198.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:198,&quot;width&quot;:836,&quot;resizeWidth&quot;:727,&quot;bytes&quot;:196301,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/187102634?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F461e32c1-3b75-4957-aa65-21bde2feab7b_836x198.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!GQeF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F461e32c1-3b75-4957-aa65-21bde2feab7b_836x198.png 424w, https://substackcdn.com/image/fetch/$s_!GQeF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F461e32c1-3b75-4957-aa65-21bde2feab7b_836x198.png 848w, https://substackcdn.com/image/fetch/$s_!GQeF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F461e32c1-3b75-4957-aa65-21bde2feab7b_836x198.png 1272w, https://substackcdn.com/image/fetch/$s_!GQeF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F461e32c1-3b75-4957-aa65-21bde2feab7b_836x198.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>One gotcha with cameras: the get method does not always return accurate frame rate values. It often returns 0. If you need precise timing, you are better off measuring the actual frame rate yourself or assuming a reasonable value, such as 30 fps.</p><h2>Displaying Results and Handling User Input </h2><p>To debug and build interactive applications, you need to be able to display images and videos. OpenCV provides a simple windowing system for this purpose. The <code>imshow</code> function displays an image in a window:</p><pre><code><code>import cv2

img = cv2.imread('photo.png')
cv2.imshow('My Window', img)
cv2.waitKey()
cv2.destroyAllWindows()
</code></code></pre><p>The <code>waitKey</code> function is often confusing for beginners. Without such a function, the window would open and then close right away. It pauses the execution and waits for a key to be pressed. You can give it a timeout in milliseconds as an argument. Passing 0 will cause it to wait forever. Passing a positive number, it will cause it to wait for that many milliseconds before continuing. </p><p>If the user doesn't press any keys before the timeout, <code>waitKey</code> returns -1. Otherwise, it returns the ASCII code of the key that was pressed. You can use this mechanism to make your application respond to keyboard input:</p><pre><code><code>while True:
    cv2.imshow('My Window', frame)
    key = cv2.waitKey(1)
    if key == 27:  # ESC key
        break
</code></code></pre><p>For mouse input, you need to register a callback function using <code>setMouseCallback</code>. The callback gets the kind of event, the x and y coordinates, and some extra flags:</p><pre><code><code>import cv2

def on_mouse(event, x, y, flags, param):
    if event == cv2.EVENT_LBUTTONUP:
        print(f'Clicked at ({x}, {y})')

cv2.namedWindow('My Window')
cv2.setMouseCallback('My Window', on_mouse)

img = cv2.imread('photo.png')
while True:
    cv2.imshow('My Window', img)
    if cv2.waitKey(1) == 27:
        break

cv2.destroyAllWindows()
</code></code></pre><p>The event types are EVENT_MOUSEMOVE for movement, LBUTTONDOWN and LBUTTONUP for left-click, and similar events for right- and middle-click.</p><p>One important detail: OpenCV windows only update when <code>waitKey</code> is called. If you forget to call it inside your loop, you will see a frozen or blank window. In the same way, <code>waitKey</code> only captures keyboard input when an OpenCV window has focus.</p><h2>Conclusion</h2><p>We covered a lot of basic OpenCV ground in this article. You learned that images are just NumPy arrays, how to load and save them with <code>imread</code> and <code>imwrite</code>, how to work with video files and camera input using <code>VideoCapture</code> and <code>VideoWriter</code>, and how to display results and handle user input with OpenCV&#8217;s windowing system.</p><p>These are the building blocks for everything else you will do. If you want to go deeper, I suggest playing around with the code samples I used for this article, which I put in this <a href="https://github.com/FrancoFernando/computer-vision-mastery">GitHub repo</a>. Try loading images having different formats, recording video from your webcam, or making a small app that responds to mouse clicks. The best way to get this concepts is to run code and see what happens.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.francofernando.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">The Polymathic Engineer is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[How to Navigate Performance Reviews Like a Pro]]></title><description><![CDATA[A guide to understanding the system, preparing strategically, and getting the rating you deserve.]]></description><link>https://newsletter.francofernando.com/p/how-to-navigate-performance-reviews</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/how-to-navigate-performance-reviews</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Sat, 21 Feb 2026 14:38:53 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ukQY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F907bb9af-1e69-436c-9645-c8e42f13c039_1057x584.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends,</p><p>Welcome to the 161st issue of the Polymethic Engineer newsletter. </p><p>Regardless of whether you are an experienced software engineer or are earlier in your career, performance reviews can be scary. There are so many ways they cause anxiety: you get graded as an employee, often compared directly to your peers, and in some big tech companies, the bottom 5-10% get cut every cycle.</p><p>But here&#8217;s the thing: performance reviews are a system. And like any system, once you understand how it works, you can learn to navigate it effectively.</p><p>Many engineers treat performance reviews as something that happens to them. They put in the hours, work hard, avoid negative feedback, and hope for the best. However, this is a dangerous mentality. It can work sometimes, but the more important thing to think about is when it doesn&#8217;t. And you want to minimize that as much as possible.</p><p>The engineers who consistently get strong ratings and promotions aren&#8217;t just better at coding. They understand how the game is played. They know how they are measured, they prepare strategically, and they make sure their work is visible to the right people.</p><p>In this issue, we break down the recipe for succeeding within a performance review:</p><ul><li><p>Understanding the Yardstick</p></li><li><p>The three types of performance review systems</p></li><li><p>Preparing for success before the cycle begins</p></li><li><p>Executing during the cycle</p></li><li><p>Writing feedback that matters (for yourself and others)</p></li></ul><div><hr></div><p>Project-based learning is the best way to develop technical skills. <a href="https://app.codecrafters.io/join?via=FrancoFernando">CodeCrafters </a>is an excellent platform for tackling exciting projects, such as building your own Redis, Kafka, a <a href="https://app.codecrafters.io/join/dns-server?via=francofernando">DNS server</a>, SQLite, or Git from scratch. <a href="https://app.codecrafters.io/join?via=FrancoFernando">Sign up, and become a better software engineer</a>.</p><div><hr></div><h2>Understanding the Yardstick</h2><p>The output of a performance review system is typically a rating. Think of it like school grades: you need to understand what the meaning of each rating is, what is considered average, and what you need to do to get promoted.</p><p>For example, at Meta, the rating system looks like this:</p><ul><li><p><strong>Meets Some:</strong> You are doing quite poorly. Getting this rating usually means you will be put on a PIP or even fired.</p></li><li><p><strong>Meets Most:</strong> You are not meeting all expectations. Two or more Meets Most ratings typically lead to a PIP.</p></li><li><p><strong>Meets All:</strong> The default rating. If you just joined the company or are doing solid but unremarkable work, this is where you land.</p></li><li><p><strong>Greatly Exceeds:</strong> One level up from Meets All. This is quite hard to achieve.</p></li><li><p><strong>Redefines:</strong> The top rating, significantly harder than Greatly Exceeds.</p></li></ul><p>Most other companies have a similar system. However, there is a part that surprises many engineers: the distribution of ratings is uneven. The bottom two ratings usually apply to 5-10% of employees. Around 40% get the middle rating, another 40% get the first &#8220;exceeds&#8221; tier, and only 10-15% reach the top rating.</p><p>This distribution also changes as you become more senior. At the junior level, it is common for most engineers to receive an &#8220;exceeds&#8221; rating because expectations are lower. But at senior levels, getting that same rating becomes much harder, and 60-70% of engineers end up with the equivalent of &#8220;Meets All.&#8221;</p><p>And here is the counterintuitive part: a Meets All rating does not mean you are doing well. It means you are effectively in the bottom half of your level. If you are aiming for promotion, Meets Al is not enough. In fact, it is almost a bad signal because it means you're not standing out from your peers.</p><p>So the first step is to understand your company&#8217;s rating system and ask yourself: What are the expectations for my level? Which rating is considered average? What is needed for promotion? The answers will be different for each company and each level.</p><h2>The Three Types of Performance Review Systems</h2><p>There are different kinds of performance review systems. Companies usually fit into one of three groups based on how they gather feedback and the power their managers have.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ukQY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F907bb9af-1e69-436c-9645-c8e42f13c039_1057x584.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ukQY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F907bb9af-1e69-436c-9645-c8e42f13c039_1057x584.png 424w, https://substackcdn.com/image/fetch/$s_!ukQY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F907bb9af-1e69-436c-9645-c8e42f13c039_1057x584.png 848w, https://substackcdn.com/image/fetch/$s_!ukQY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F907bb9af-1e69-436c-9645-c8e42f13c039_1057x584.png 1272w, https://substackcdn.com/image/fetch/$s_!ukQY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F907bb9af-1e69-436c-9645-c8e42f13c039_1057x584.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ukQY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F907bb9af-1e69-436c-9645-c8e42f13c039_1057x584.png" width="568" height="313.8240302743614" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/907bb9af-1e69-436c-9645-c8e42f13c039_1057x584.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:584,&quot;width&quot;:1057,&quot;resizeWidth&quot;:568,&quot;bytes&quot;:144404,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/187569667?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F907bb9af-1e69-436c-9645-c8e42f13c039_1057x584.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ukQY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F907bb9af-1e69-436c-9645-c8e42f13c039_1057x584.png 424w, https://substackcdn.com/image/fetch/$s_!ukQY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F907bb9af-1e69-436c-9645-c8e42f13c039_1057x584.png 848w, https://substackcdn.com/image/fetch/$s_!ukQY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F907bb9af-1e69-436c-9645-c8e42f13c039_1057x584.png 1272w, https://substackcdn.com/image/fetch/$s_!ukQY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F907bb9af-1e69-436c-9645-c8e42f13c039_1057x584.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The<strong> ad hoc systems </strong>are the worst type. There is no regular framework, and feedback only happens when something major occurs. Maybe you made a significant mistake, or you told your boss you have a competing offer. Suddenly, they scramble to tell you how valuable you are and offer you a raise to stay.</p><p>This approach is common at small startups and consulting shops. The problem is that it rewards the squeaky wheel. Engineers who know how to complain and play politics make more money than those who just do their jobs.</p><p>In <strong>manager-dominated</strong> systems, performance assessments are conducted regularly, but your manager has full authority over your rating, compensation, and promotion. This is common in older companies, especially those where software engineers work in the IT department.</p><p>The problem here is that your career is based on what one individual thinks. You do well if your boss likes you. If they don't, or if they just don't speak out for you, your growth will stop, no matter how amazing your job is.</p><p>The<strong> 360-degree feedback system</strong> is what modern tech companies use. The idea is that feedback comes from all directions:</p><ul><li><p>From within: you write a self-review</p></li><li><p>From above: your manager writes feedback about you</p></li><li><p>From the side: peers you have worked with share their perspective</p></li><li><p>From below: if you lead a team, your reports evaluate your leadership</p></li></ul><p>The benefit of this approach is that it provides the clearest picture of how well you are doing. It also prevents one individual from having too much control over your career. Strong peer feedback might make up for your manager not valuing you enough. If you say you are a great team player, but your partners from other departments don't agree, that will show up too.</p><p>If your company does not have a 360-degree feedback system, think about introducing it yourself. You can ask your coworkers for feedback regularly via email or Slack. Ask them how you could improve on certain projects. If you can create a culture of organic feedback requests, that is a powerful thing for both you and your team.</p><h2>Why Promotions Should Be Lagging</h2><p>It took me a while to get this concept, but it changed how I think about career growth.</p>
      <p>
          <a href="https://newsletter.francofernando.com/p/how-to-navigate-performance-reviews">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Authentication Fundamentals: Part II ]]></title><description><![CDATA[OAuth2 and JWT: the Modern API Authentication Standards.]]></description><link>https://newsletter.francofernando.com/p/authentication-fundamentals-part-ac6</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/authentication-fundamentals-part-ac6</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Sat, 14 Feb 2026 08:36:21 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!9hEP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2209955-b56b-4f7b-a208-dac997f68dff_1356x670.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends,</p><p>Welcome to the 160th issue of the Polymathic Engineer newsletter. This is the second article in our series on authentication and authorization.</p><p>In the last article, we talked about a major issue with API keys. They can be used to identify applications, but they can&#8217;t speak for users safely. You have to make a tough decision if a third-party app needs to do something for a user: either fully trust the third party or ask users to share their passwords.</p><p>Neither choice is good enough. As long as the third party has blind trust, they can pretend to be any user. If people share passwords, a third party can always get into user accounts. All user credentials are at risk if there is a single breach at the third party. </p><p>OAuth2 solves this problem. It is an authorization framework that lets users grant limited access to their data without sharing credentials. A user can say, &#8220;This app can read my email, but it can&#8217;t delete anything,&#8221; without giving away their password. </p><p>As of now, OAuth2 is the usual way to authorize APIs. It&#8217;s used by Google, Facebook, Microsoft, GitHub, and thousands of other big platforms. You need to know about OAuth2 if you are building APIs that third parties will use. </p><p>In this article, we'll cover:</p><ul><li><p>OAuth2 fundamentals and the four roles</p></li><li><p>Authorization Code Grant for web applications</p></li><li><p>Authorization Code Grant + PKCE for mobile apps and SPAs</p></li><li><p>Client Credentials Grant for machine-to-machine communication</p></li><li><p>JSON Web Tokens (JWT) as the standard token format</p></li><li><p>Refresh tokens for seamless user experience</p></li><li><p>PASETO as a modern alternative to JWT</p></li></ul><div><hr></div><p>Project-based learning is the best way to develop technical skills. <a href="https://app.codecrafters.io/join?via=FrancoFernando">CodeCrafters </a>is an excellent platform for tackling exciting projects, such as building your own Redis, Kafka, a <a href="https://app.codecrafters.io/join/dns-server?via=francofernando">DNS server</a>, SQLite, or Git from scratch. <a href="https://app.codecrafters.io/join?via=FrancoFernando">Sign up, and become a better software engineer</a>.</p><div><hr></div><h2><strong>OAuth2 Fundamentals</strong></h2><p>The first thing to keep in mind is that OAuth2 is not an authentication system, but a framework for authorizing access. This distinction is critical. OAuth2 tells you what a user has allowed an application to do. It doesn&#8217;t directly identify the user.</p><p>Here is a typical scenario that OAuth2 addresses. Let&#8217;s say a user wants to use a third-party scheduling tool to manage their social media post submissions. The scheduling tool needs to access the users&#8217; social media profile data. Without using OAuth2, the user would need to give their social media password to the scheduling tool.</p><p>With OAuth2, the user can grant specific permissions to the scheduling tool without sharing credentials. The speaker might say, &#8220;You can read my profile and write posts, but you can&#8217;t access my payment information.&#8221; If the user later wants to revoke access, they can do so without changing their password.</p><p>OAuth2 defines four distinct roles in the authorization process:</p><ul><li><p><strong>Resource Owner</strong> is typically an end user who owns the data. In our example, this is the user who has a profile in the social media app. The resource owner decides what access to grant to third-party applications.</p></li><li><p><strong>Authorization Server</strong> gives out access tokens after verifying the resource owner's identity and getting their permission. Most identity providers, such as Google, Microsoft, and Auth0, are OAuth 2.0 authorization servers. Servers have two endpoints: the authorization endpoint (where users give consent) and the token endpoint (where applications get access tokens).</p></li><li><p><strong>Client</strong> is the application requesting access to protected resources. In our example, this is the scheduling tool. There are two types of clients: confidential clients can maintain secrets (like web applications with server backends), and public clients cannot (like single-page applications and mobile apps).</p></li><li><p><strong>The Resource Server</strong> hosts protected resources and accepts requests using access tokens. In our example, this is the social media API. The resource server validates tokens and serves data if the token is valid and has appropriate permissions.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9hEP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2209955-b56b-4f7b-a208-dac997f68dff_1356x670.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9hEP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2209955-b56b-4f7b-a208-dac997f68dff_1356x670.png 424w, https://substackcdn.com/image/fetch/$s_!9hEP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2209955-b56b-4f7b-a208-dac997f68dff_1356x670.png 848w, https://substackcdn.com/image/fetch/$s_!9hEP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2209955-b56b-4f7b-a208-dac997f68dff_1356x670.png 1272w, https://substackcdn.com/image/fetch/$s_!9hEP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2209955-b56b-4f7b-a208-dac997f68dff_1356x670.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9hEP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2209955-b56b-4f7b-a208-dac997f68dff_1356x670.png" width="716" height="353.77581120943955" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f2209955-b56b-4f7b-a208-dac997f68dff_1356x670.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1356,&quot;resizeWidth&quot;:716,&quot;bytes&quot;:268877,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/182955072?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2209955-b56b-4f7b-a208-dac997f68dff_1356x670.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!9hEP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2209955-b56b-4f7b-a208-dac997f68dff_1356x670.png 424w, https://substackcdn.com/image/fetch/$s_!9hEP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2209955-b56b-4f7b-a208-dac997f68dff_1356x670.png 848w, https://substackcdn.com/image/fetch/$s_!9hEP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2209955-b56b-4f7b-a208-dac997f68dff_1356x670.png 1272w, https://substackcdn.com/image/fetch/$s_!9hEP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2209955-b56b-4f7b-a208-dac997f68dff_1356x670.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In real-world systems, the resource server is often an API gateway that sits in front of multiple backend services. Clients call the API gateway, which works as a single entry point. The gateway validates access tokens and directs requests to the right backend services. From the client&#8217;s perspective, the API gateway is the resource server, even though multiple services are behind it. </p><h2><strong>OAuth2 Grant Mechanisms</strong></h2><p>OAuth2 provides an abstract protocol that all authorization requests follow:</p><ol><li><p>The client requests authorization from the resource owner.</p></li><li><p>The resource owner is redirected to a consent screen or another mechanism that grants or denies authorization.</p></li><li><p>The client presents the authorization grant to the authorization server and requests an access token.</p></li><li><p>The authorization server validates the grant and issues an access token if everything checks out.</p></li><li><p>The client makes a request to the resource server, including the access token in the request header.</p></li><li><p>The resource server validates the access token and returns the requested resource if the token is valid.</p></li></ol><p>This flow is meant to be flexible. Different grant types follow these steps differently, depending on their security requirements and use cases. The most important thing to remember is that the resource owner is in charge, the client never sees the user&#8217;s credentials, and the resource server only needs to verify that tokens are valid.</p><p>In order to choose the right OAuth2 grant, you need to know the difference between confidential and public clients. Confidential clients can securely store secrets. A web application with server-side code is confidential because the server can store a client secret that users never see. Using this secret, the server can prove its identity to the authorization server.  </p><p>Public clients cannot securely store secrets. A single-page application runs entirely in the browser. Users can view all JavaScript code, including secrets. The same problem exists for mobile apps since users can decompile apps.</p><p>This difference is essential because some OAuth2 grants need a client to authenticate with a secret. Confidential clients can use these grants. Clients who are open to the public need different grants that don&#8217;t depend on secrets.</p><p>In the next section, we&#8217;ll explore the Authorization Code Grant, the most secure and widely used grant for confidential clients.</p><h2><strong>Authorization Code Grant</strong></h2><p>The Authorization Code Grant is the most complete and secure OAuth2 grant type. It is made for confidential clients who can protect a secret.</p><p>The critical thing about this type of grant is that it uses an intermediate authorization code before issuing an access token. This adds a layer of security by dividing the user-facing authorization step from the server-to-server token exchange.</p>
      <p>
          <a href="https://newsletter.francofernando.com/p/authentication-fundamentals-part-ac6">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[A Practical Guide to Types of Machine Learning]]></title><description><![CDATA[Understanding supervised, unsupervised, and reinforcement learning. How to choose the right approach for your problem.]]></description><link>https://newsletter.francofernando.com/p/a-practical-guide-to-types-of-machine</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/a-practical-guide-to-types-of-machine</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Fri, 06 Feb 2026 10:12:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!cj_b!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75d2412-6e1f-44fc-b92e-a4d56e0febe0_1430x468.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends,</p><p>Welcome to the 159th issue of the Polymathic Engineer newsletter.</p><p>As you know, machine learning is everywhere. It powers the movie recommendations you see on Netflix, filters spam from your inbox, enables self-driving cars to navigate city streets, and can even help doctors diagnose diseases. However, behind all these applications lies a fundamental question: what type of machine learning should you use?</p><p>There are multiple ways to classify machine learning systems. The most common is based on the type of supervision they receive during training. However, you can also categorize them by whether they learn incrementally or all at once, or by how they generalize what they have learned from training data to new examples.</p><p>Understanding these distinctions is important because choosing the wrong approach wastes time and resources. You cannot build a spam detector system without labeled spam examples. In the same way, you can&#8217;t group customers into segments if you have already decided what those segments should be. Determining which machine learning method is right for the job is the first step to building something that works.</p><p>In this issue, we will have a look at the different types of machine learning and see the use cases they fit. The outline is as follows:</p><ul><li><p>Labeled vs Unlabeled Data: The Foundation</p></li><li><p>Supervised Learning: Predicting Labels</p></li><li><p>Unsupervised Learning: Finding Hidden Patterns</p></li><li><p>Reinforcement Learning: Learning Through Interaction</p></li><li><p>Beyond the Big Three: Other Ways to Classify ML Systems</p></li></ul><div><hr></div><p>Project-based learning is the best way to develop technical skills. <a href="https://app.codecrafters.io/join?via=FrancoFernando">CodeCrafters </a>is an excellent platform for tackling exciting projects, such as building your own Redis, Kafka, a <a href="https://app.codecrafters.io/join/dns-server?via=francofernando">DNS server</a>, SQLite, or Git from scratch. <a href="https://app.codecrafters.io/join?via=FrancoFernando">Sign up, and become a better software engineer</a>.</p><div><hr></div><h2>Labeled vs Unlabeled Data: The Foundation</h2><p>Before diving into the different types of machine learning, we need to understand the raw material that powers them all: data. In fact, machine learning is just the field that studies how to teach computers to learn from data. The examples that the system uses to learn are called the <em>training set</em>, while the<em> </em>part of the system that learn and generates predictions is known as the<em> model.</em></p><p>Data is just information put together in a structured way. If you have a table with rows and columns, you have data. Each row represents a single data point, and each column represents a <strong>feature</strong>, which is a property or trait that describes that data point.</p><p>For example, in a dataset of houses, some features include size, number of bedrooms, distance to the nearest school, and the local area crime rate. In an email dataset, the features could be the sender, the subject, the body text, or the number of attachments.</p><p>But here is where things start to get interesting. Some features stand out to us, and we refer to them as &#8220;labels.&#8221; A label is a feature we want to predict. It&#8217;s the answer we are looking for based on all the other features. The important thing is that what counts as a label depends entirely on the problem we&#8217;re trying to solve. If we&#8217;re predicting house prices, the price is the label; if we want to detect spam emails, the label is spam/not-spam classification; if we&#8217;re estimating how long it will take for a patient to recover, that time is the label.</p><p>The goal of a machine learning model is to analyze the features and produce the most accurate possible prediction of a label. This brings us to a fundamental distinction:</p><ul><li><p><strong>Labeled data</strong> comes with the answers already filled in. We know the house prices, we know which emails are spam, and we see the patient outcomes.</p></li><li><p><strong>Unlabeled data</strong> has no such answers. We have all the features, but no target to predict.</p></li></ul><p>For example, a set of emails marked as &#8220;spam&#8221; or &#8220;not spam&#8221; is labeled data. A set of emails without such tags is unlabeled.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cj_b!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75d2412-6e1f-44fc-b92e-a4d56e0febe0_1430x468.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cj_b!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75d2412-6e1f-44fc-b92e-a4d56e0febe0_1430x468.png 424w, https://substackcdn.com/image/fetch/$s_!cj_b!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75d2412-6e1f-44fc-b92e-a4d56e0febe0_1430x468.png 848w, https://substackcdn.com/image/fetch/$s_!cj_b!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75d2412-6e1f-44fc-b92e-a4d56e0febe0_1430x468.png 1272w, https://substackcdn.com/image/fetch/$s_!cj_b!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75d2412-6e1f-44fc-b92e-a4d56e0febe0_1430x468.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cj_b!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75d2412-6e1f-44fc-b92e-a4d56e0febe0_1430x468.png" width="1430" height="468" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e75d2412-6e1f-44fc-b92e-a4d56e0febe0_1430x468.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:468,&quot;width&quot;:1430,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:162565,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/186289222?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75d2412-6e1f-44fc-b92e-a4d56e0febe0_1430x468.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!cj_b!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75d2412-6e1f-44fc-b92e-a4d56e0febe0_1430x468.png 424w, https://substackcdn.com/image/fetch/$s_!cj_b!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75d2412-6e1f-44fc-b92e-a4d56e0febe0_1430x468.png 848w, https://substackcdn.com/image/fetch/$s_!cj_b!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75d2412-6e1f-44fc-b92e-a4d56e0febe0_1430x468.png 1272w, https://substackcdn.com/image/fetch/$s_!cj_b!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe75d2412-6e1f-44fc-b92e-a4d56e0febe0_1430x468.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Why does this matter? Because the type of data you have determines which machine learning approach you can use. Labeled data enables <strong>supervised learning, </strong>where<strong> </strong>the model learns from examples with known correct answers. When you give a model unlabeled data, it has to learn on its own, without being told what to look for. This is called <strong>unsupervised learning</strong>. This distinction is the foundation for everything that follows.</p><h2>Supervised Learning: Predicting Labels</h2><p>Supervised learning is the most widely used type of machine learning. It&#8217;s what makes picture recognition, spam filters, recommendation systems, and a massive number of other applications we use every day possible. The defining characteristic is simple: the training data includes the answers we want the model to learn.</p><p>In supervised learning, the model follows a remember-formulate-predict framework. The first thing it does is load the training dataset and memorize the examples. Then it comes up with rules or patterns that link the labels to the features. Lastly, when a new piece of data comes in, the model uses those rules to guess what the label will be.</p><p>For example, a spam filtering system looks at thousands of emails that have already been marked as spam or not spam to figure out what patterns make them different. It then uses those patterns to classify new incoming emails.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WUk_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3491c50-628c-4be7-8377-f436adea755e_910x528.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WUk_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3491c50-628c-4be7-8377-f436adea755e_910x528.png 424w, https://substackcdn.com/image/fetch/$s_!WUk_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3491c50-628c-4be7-8377-f436adea755e_910x528.png 848w, https://substackcdn.com/image/fetch/$s_!WUk_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3491c50-628c-4be7-8377-f436adea755e_910x528.png 1272w, https://substackcdn.com/image/fetch/$s_!WUk_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3491c50-628c-4be7-8377-f436adea755e_910x528.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WUk_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3491c50-628c-4be7-8377-f436adea755e_910x528.png" width="493" height="286.0483516483516" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e3491c50-628c-4be7-8377-f436adea755e_910x528.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:528,&quot;width&quot;:910,&quot;resizeWidth&quot;:493,&quot;bytes&quot;:71530,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/186289222?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3491c50-628c-4be7-8377-f436adea755e_910x528.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!WUk_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3491c50-628c-4be7-8377-f436adea755e_910x528.png 424w, https://substackcdn.com/image/fetch/$s_!WUk_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3491c50-628c-4be7-8377-f436adea755e_910x528.png 848w, https://substackcdn.com/image/fetch/$s_!WUk_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3491c50-628c-4be7-8377-f436adea755e_910x528.png 1272w, https://substackcdn.com/image/fetch/$s_!WUk_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3491c50-628c-4be7-8377-f436adea755e_910x528.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Supervised learning problems fall into two categories, based on what we want to guess.</p><p><strong>Regression</strong> models predict numerical values. The output is a number that can take on many possible values. An example of a regression problem is predicting how much a house will cost based on its size and location. Other examples might be estimating how long a customer will stay on a website or forecasting stock prices. The most used regression method is linear regression, which fits a line (or a higher-dimensional surface) to the data. Other popular approaches include decision trees and ensemble methods like random forests and gradient boosting.</p><p><strong>Classification</strong> models predict categories. There are only a limited number of possible states for the output. Finding spam is like dividing emails into two groups: those that are spam and those that are not. Image recognition is also classification. Given an image, the model tells you whether it contains a cat, a dog, a car, or another category. Logic regression, decision trees, support vector machines, naive Bayes, and neural networks are all common classification methods.</p><p>The line between regression and classification is not always rigid. Some algorithms can do both things with only small changes. For example, logistic regression<em> </em>produces numeric values representing the probability of belonging to a given category, making it commonly used for classification.</p><p>Supervised learning can be very accurate when there are many well-labeled examples. Labels, on the other hand, can be pricey. Someone has to mark those emails as spam by hand, put those pictures into the right category, or write down the actual prices of the homes for sale. Supervised learning might not work in domains where marking is hard or expensive, at least not without help from other methods. </p><p>This is where semi-supervised learning comes in. There are algorithms that can work with data that is only partly labeled, meaning that many cases aren&#8217;t labeled and only a few are. Photo-hosting services such as Google Photos use this method. The system clusters similar faces together (unsupervised), so you only need to assign each person a single label. Semi-supervised learning methods combine the pattern-finding power of unsupervised methods with the precision of labeled examples.</p>
      <p>
          <a href="https://newsletter.francofernando.com/p/a-practical-guide-to-types-of-machine">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Authentication Fundamentals: Part I ]]></title><description><![CDATA[From Passwords to Modern Approaches: sessions-cookies, tokens, API keys, OTP, and SSO.]]></description><link>https://newsletter.francofernando.com/p/authentication-fundamentals-part</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/authentication-fundamentals-part</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Sat, 31 Jan 2026 07:19:25 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!T4nR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa771bca4-6960-4a3c-adf0-be4a28afa572_875x700.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends,</p><p>Welcome to the 158th issue of the Polymathic Engineer newsletter. This week, we are starting a series of in-depth articles on two topics that every developer deals with, but few really understand: authentication and authorization.</p><p>Authentication asks &#8220;Who are you?&#8221; while authorization asks &#8220;What are you allowed to do?&#8221; The first checks identities, while the latter defines permissions.</p><p>Even if you are familiar with login forms, password validation, and user sessions, do you actually know what&#8217;s going on behind the scenes? Do you know when to utilize tokens instead of passwords, or why OAuth2 is now the norm in the industry?</p><p>The stakes are really high. An authentication system that doesn&#8217;t work properly can expose private user data, let unauthorized people in, and damage your application&#8217;s reputation.</p><p>In this series, we will look at many different ways to authenticate, from old-fashioned passwords to new solutions that don&#8217;t use passwords. We&#8217;ll look at the pros and cons of each, as well as how they can be used in practice. By the end, you will know how to pick the best method for your needs.</p><p>Here&#8217;s what we&#8217;ll cover in this first article:</p><ul><li><p>Password-based authentication</p></li><li><p>HTTP Basic Access Authentication</p></li><li><p>Session-cookie authentication</p></li><li><p>Token-based authentication</p></li><li><p>API Keys: A Simple Token Approach</p></li><li><p>One-Time Passwords (OTP)</p></li><li><p>Time-Based One-Time Password (TOTP)</p></li><li><p>Federated identity and Single Sign-On (SSO)</p></li></ul><div><hr></div><p>Project-based learning is the best way to develop technical skills. <a href="https://app.codecrafters.io/join?via=FrancoFernando">CodeCrafters </a>is an excellent platform for tackling exciting projects, such as building your own Redis, Kafka, a <a href="https://app.codecrafters.io/join/dns-server?via=francofernando">DNS server</a>, SQLite, or Git from scratch. <a href="https://app.codecrafters.io/join?via=FrancoFernando">Sign up, and become a better software engineer</a>.</p><div><hr></div><h2><strong>Password-Based Authentication</strong></h2><p>Passwords are the most traditional form of authentication. The way they work is very straightforward: you check someone&#8217;s identity by verifying if they can type a secret phrase. You let them in if they know it. If they know it, you grant access.</p><p>This approach has been around since the first computers, and we use it for everything from email to banking apps. But making password-based authentication safe is harder than it seems. Even though passwords seem straightforward, they are challenging for both users and developers.</p><p>Most people still rely on memorization or handwritten notes for their passwords. This means they frequently forget or lose their passwords, so you have to have a password recovery flow in place. Users need a way to reset their password and then show who they are through email or text message. This makes your system more complicated and could leave it vulnerable to security holes.</p><p>Also, reusing passwords is very common. Most people use the same password across multiple sites if they don&#8217;t have a password manager. When there is a data breach at one service, that problem becomes yours. If people who use your site don&#8217;t change their passwords, anyone with access to credential files that have been leaked can access their accounts.</p><p>The <a href="https://en.wikipedia.org/wiki/23andMe_data_leak">23AndMe breac</a>h demonstrated this perfectly. Hackers didn&#8217;t need to break into any system. They just tested leaked passwords from other breaches against 23AndMe accounts. 7 million accounts of users who had reused passwords were compromised.</p><p>But password management is complex for developers. You need to hash passwords securely, not store them in plain text. You must use strong hashing algorithms and add unique salts to each password to protect against rainbow table attacks. You need to implement password strength requirements, handle password resets securely, and protect against brute-force attacks.</p><p>Thankfully, third-party tools such as AWS Cognito and Firebase can handle much of this complexity. However, even with these tools, you still have to deal with the basic weaknesses of password-based authentication.</p><h2>HTTP Basic Access Authentication</h2><p>Let&#8217;s start with the simplest password authentication mechanism: HTTP Basic Access Authentication. When a client tries to access a protected resource, the server returns a 401 Unauthorized status code and includes the Authorization: Basic header. This lets the server inform the client that it needs to provide credentials.</p><p>The client then prompts the user for a username and password. These credentials are combined into a single Base64-encoded string in the format username: password and sent back to the server in the Authorization: Basic header.</p><p>The server then decodes the Base64 string, splits the username and password, and compares them against its list of users. If the credentials are correct, the user can get in. If not, the server returns a second 401 response.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!T4nR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa771bca4-6960-4a3c-adf0-be4a28afa572_875x700.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!T4nR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa771bca4-6960-4a3c-adf0-be4a28afa572_875x700.png 424w, https://substackcdn.com/image/fetch/$s_!T4nR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa771bca4-6960-4a3c-adf0-be4a28afa572_875x700.png 848w, https://substackcdn.com/image/fetch/$s_!T4nR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa771bca4-6960-4a3c-adf0-be4a28afa572_875x700.png 1272w, https://substackcdn.com/image/fetch/$s_!T4nR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa771bca4-6960-4a3c-adf0-be4a28afa572_875x700.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!T4nR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa771bca4-6960-4a3c-adf0-be4a28afa572_875x700.png" width="579" height="463.2" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a771bca4-6960-4a3c-adf0-be4a28afa572_875x700.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:700,&quot;width&quot;:875,&quot;resizeWidth&quot;:579,&quot;bytes&quot;:94953,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/183545746?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa771bca4-6960-4a3c-adf0-be4a28afa572_875x700.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!T4nR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa771bca4-6960-4a3c-adf0-be4a28afa572_875x700.png 424w, https://substackcdn.com/image/fetch/$s_!T4nR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa771bca4-6960-4a3c-adf0-be4a28afa572_875x700.png 848w, https://substackcdn.com/image/fetch/$s_!T4nR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa771bca4-6960-4a3c-adf0-be4a28afa572_875x700.png 1272w, https://substackcdn.com/image/fetch/$s_!T4nR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa771bca4-6960-4a3c-adf0-be4a28afa572_875x700.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here is the key problem: Base64 encoding is not encryption. Anyone who intercepts the request can easily decode the credentials. It is like sharing your password in plain text with only a little cover.</p><p>Modern applications solve this by using HTTPS, which encrypts all data between the browser and server. But even with this approach, HTTP Basic Access Authentication still has limitations. The browser sends credentials with every request to protected resources within the same domain. This makes things easier for users, but it means that passwords are always being sent across the network.</p><p>This method of authentication isn&#8217;t beneficial for current websites. However, it is helpful to understand because it shows the main problem: how do we make sure people are who they say they are without sending passwords across the network all the time?</p><h2>Session-Cookie Authentication</h2><p>Session-cookie authentication has been introduced with the goal of minimizing the number of password transmissions across the network.</p><p>The core idea is simple: users enter their password once during login. The server then issues a session ID that represents their authenticated state. This session ID travels with subsequent requests instead of the password. Think of it like a hotel key card. You show your ID at check-in (authentication), and they give you a key card (session ID). You don&#8217;t need to show your ID every time you enter your room; you use the key card.</p><p>But what is the role of Cookies? When we discussed how to build stateless services, we saw that cookies are small pieces of data that web servers store in your browser.</p><p>When a server sends a cookie to your browser, the browser automatically includes it in every subsequent request to that domain. This allows the server to maintain state across multiple requests.</p><p>Cookies serve many purposes: tracking logged-in users, storing preferences, and recording user behavior. For authentication, cookies typically store session identifiers.</p><p>Here is the complete flow:</p><ol><li><p><strong>User Login. </strong>You submit your username and password to the server. The server validates these credentials against its user database. If they match, the server creates a session for you.</p></li><li><p><strong>Session Creation. </strong>The server generates a unique session ID that serves as the key to your authenticated state. The server stores session data on the server side. This might include your user ID, roles, preferences, and when the session was created.</p></li><li><p><strong>Session ID Delivery. </strong>The server sends the session ID to your browser in a Set-Cookie header. Your browser stores this cookie automatically.</p></li></ol>
      <p>
          <a href="https://newsletter.francofernando.com/p/authentication-fundamentals-part">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Software Design Principles That Matter]]></title><description><![CDATA[A tour over Open-Closed Principle, Dependency Injection, and Inversion of Control.]]></description><link>https://newsletter.francofernando.com/p/software-design-principles-that-matter</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/software-design-principles-that-matter</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Fri, 23 Jan 2026 10:23:01 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/9d45849c-e385-43bd-9ed4-a8d598f29b05_657x337.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends,</p><p>Welcome to the 157th issue of the Polymathic Engineer.</p><p>Every software engineer has seen something like this happen: you write a feature, ship it to production, and everything works great. Then, a few weeks later, you get different requirements. You have to support a new use case, but you find out that your code is not ready.</p><p>You open the codebase and realize that to make this change, you need to update many classes and method signatures, and you hope you don&#8217;t break anything in the process. </p><p>What should have been a simple extension turns into a risky refactoring project. This is where good software design principles make all the difference.</p><p>In this article, we&#8217;ll explore three fundamental principles that help you build flexible, maintainable code: the <strong>Open-Closed Principle, Dependency Injection, and Inversion of Control</strong>. </p><p>By the end, you will hopefully have a better understanding of how to write code that welcomes change rather than fights against it.</p><div><hr></div><p>This O&#8217;Reilly book on data engineering patterns is a great way to start learning about how to build large-scale APIs and data pipelines. You can <a href="https://fandf.co/3LtVWZM">get it for free</a> thanks to the <a href="https://buf.build/home">Buf </a>team, who sponsored this newsletter issue. </p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!z35d!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3415b1c7-6d17-47fb-baac-e974ea63b646_360x520.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!z35d!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3415b1c7-6d17-47fb-baac-e974ea63b646_360x520.png 424w, https://substackcdn.com/image/fetch/$s_!z35d!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3415b1c7-6d17-47fb-baac-e974ea63b646_360x520.png 848w, https://substackcdn.com/image/fetch/$s_!z35d!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3415b1c7-6d17-47fb-baac-e974ea63b646_360x520.png 1272w, https://substackcdn.com/image/fetch/$s_!z35d!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3415b1c7-6d17-47fb-baac-e974ea63b646_360x520.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!z35d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3415b1c7-6d17-47fb-baac-e974ea63b646_360x520.png" width="140" height="202.22222222222223" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3415b1c7-6d17-47fb-baac-e974ea63b646_360x520.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:520,&quot;width&quot;:360,&quot;resizeWidth&quot;:140,&quot;bytes&quot;:208115,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/182004674?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3415b1c7-6d17-47fb-baac-e974ea63b646_360x520.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!z35d!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3415b1c7-6d17-47fb-baac-e974ea63b646_360x520.png 424w, https://substackcdn.com/image/fetch/$s_!z35d!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3415b1c7-6d17-47fb-baac-e974ea63b646_360x520.png 848w, https://substackcdn.com/image/fetch/$s_!z35d!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3415b1c7-6d17-47fb-baac-e974ea63b646_360x520.png 1272w, https://substackcdn.com/image/fetch/$s_!z35d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3415b1c7-6d17-47fb-baac-e974ea63b646_360x520.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><br>Buf helps teams manage their Protobuf APIs. Manually generating `.proto&#8217; files and putting them under version control works at a small scale. But if you have several teams using Protobuf and lots of API users that need to pin to specific versions, it gets hard. </p><p>The <a href="https://buf.build/product/bsr">&#120277;&#120322;&#120307; &#120294;&#120304;&#120309;&#120306;&#120314;&#120302; &#120293;&#120306;&#120308;&#120310;&#120320;&#120321;&#120319;&#120326;</a> provides centralized API management for Protobuf APIs, giving you a single source of truth and policy enforcement.</p><p><a href="https://fandf.co/3LtVWZM">Download your free copy if the book here.</a></p><div><hr></div><h2>Open-Closed Principle</h2><p>The Open-Closed Principle states that code should be &#8220;open for extension, closed for modification. This means you shouldn&#8217;t have to change the code to add new features. </p><p>The goal is to write code that doesn&#8217;t need changes when requirements evolve. What this means can be better understood through an example. </p><p>Let&#8217;s suppose you are building a payment processing system. At first, the requirement is simple: handle credit card charges. You might start creating a CreditCardProcessor class that handles everything:</p><pre><code>public class CreditCardProcessor
{
    public void ProcessPayment(decimal amount, string cardNumber)
    {
        if (!IsValidCard(cardNumber))
            throw new InvalidCardException();
            
        ChargeCard(cardNumber, amount);
        
        LogTransaction("CreditCard", amount);
    }
    
    private bool IsValidCard(string cardNumber) { ... }
    private void ChargeCard(string cardNumber, decimal amount) { ... }
    private void LogTransaction(string type, decimal amount) { ... }
}</code></pre><p>This works fine until you get a new requirement: support PayPal payments. Then you get another one: support Stripe. Now you are stuck.</p><p>It's no longer just about credit cards, so adding all this logic to CreditCardProcessor doesn't make sense. Also, copying and pasting the class to create PayPalProcessor and StripeProcessor doesn't make sense, since it would mean writing the same code twice. </p><p>There is no good choice. The solution is to separate the parts that vary from the parts that stay the same. You can create an interface for payment methods, then implement it for each payment type:</p><pre><code>public interface IPaymentMethod
{
    bool Validate();
    void Charge(decimal amount);
    string GetPaymentType();
}

public class CreditCardPayment : IPaymentMethod
{
    private string _cardNumber;
    
    public CreditCardPayment(string cardNumber)
    {
        _cardNumber = cardNumber;
    }
    
    public bool Validate()
    {
        return IsValidCard(_cardNumber);
    }
    
    public void Charge(decimal amount)
    {
        ChargeCard(_cardNumber, amount);
    }
    
    public string GetPaymentType() =&gt; "CreditCard";
    
    private bool IsValidCard(string cardNumber) { ... }
    private void ChargeCard(string cardNumber, decimal amount) { ... }
}

public class PayPalPayment : IPaymentMethod
{
    private string _email;
    
    public PayPalPayment(string email)
    {
        _email = email;
    }
    
    public bool Validate()
    {
        return IsValidEmail(_email);
    }
    
    public void Charge(decimal amount)
    {
        ChargePayPal(_email, amount);
    }
    
    public string GetPaymentType() =&gt; "PayPal";
    
    private bool IsValidEmail(string email) {...}
    private void ChargePayPal(string email, decimal amount) {...}
}
</code></pre><p>Now, to add support for a different payment method, all you have to do is make a new class that uses IPaymentMethod. You don&#8217;t change PaymentProcessor or any payment methods that are already set up. The PaymentProcessor is closed for modification but open for extension. The principle allows you to keep more options available and delay decisions about the details, so you don't have to change as much code.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lCuq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdad0cf-91ad-45f6-b472-6aa02b94e960_2109x1212.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lCuq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdad0cf-91ad-45f6-b472-6aa02b94e960_2109x1212.png 424w, https://substackcdn.com/image/fetch/$s_!lCuq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdad0cf-91ad-45f6-b472-6aa02b94e960_2109x1212.png 848w, https://substackcdn.com/image/fetch/$s_!lCuq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdad0cf-91ad-45f6-b472-6aa02b94e960_2109x1212.png 1272w, https://substackcdn.com/image/fetch/$s_!lCuq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdad0cf-91ad-45f6-b472-6aa02b94e960_2109x1212.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lCuq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdad0cf-91ad-45f6-b472-6aa02b94e960_2109x1212.png" width="586" height="336.8695054945055" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/efdad0cf-91ad-45f6-b472-6aa02b94e960_2109x1212.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:837,&quot;width&quot;:1456,&quot;resizeWidth&quot;:586,&quot;bytes&quot;:197549,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/182004674?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdad0cf-91ad-45f6-b472-6aa02b94e960_2109x1212.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lCuq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdad0cf-91ad-45f6-b472-6aa02b94e960_2109x1212.png 424w, https://substackcdn.com/image/fetch/$s_!lCuq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdad0cf-91ad-45f6-b472-6aa02b94e960_2109x1212.png 848w, https://substackcdn.com/image/fetch/$s_!lCuq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdad0cf-91ad-45f6-b472-6aa02b94e960_2109x1212.png 1272w, https://substackcdn.com/image/fetch/$s_!lCuq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefdad0cf-91ad-45f6-b472-6aa02b94e960_2109x1212.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Good examples of the open-closed principle include MVC frameworks such as Spring or ASP.NET Core. These frameworks have also ruled web development because they are easy to use and can be extended. You don't have to change the framework's code to add new tools, create custom filters, or set up custom model binders. </p><p>The framework defines extension points through interfaces and abstract classes. You plug in your implementations, and everything works together.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.francofernando.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">The Polymathic Engineer is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>Dependency Injection</strong></h2><p>Dependency Injection is an excellent technique for reducing coupling and promoting the open-closed principle. Instead of having the class create its own dependencies, it provides references to the things the class needs.</p><p>When using dependency injection, you try to know as little as possible. Classes don&#8217;t need to know how their dependencies are created, where they come from, or which implementations are being used. It may not seem like a big deal, but this small change makes your code much more flexible.</p><p>As an analogy, think about how a streaming player, like Netflix or Spotify, works. The player knows how to display content, control playback, and interact with users. But it doesn&#8217;t know about every movie or song that exists. It doesn&#8217;t have a hardcoded list of all available content.</p><p>The app instead relies on an external information source. Netflix gets the library from a server when you open it. The player stays dumb and doesn't need any change when a new movie comes out.</p><p>Let&#8217;s look at a code example. Here is an implementation for a notification service that sends emails:</p><pre><code>public class NotificationService
{
    private EmailSender _emailSender;
    
    public NotificationService()
    {
        _emailSender = new EmailSender("smtp.gmail.com", 587);
    }
    
    public void NotifyUser(string email, string message)
    {
        _emailSender.Send(email, message);
    }
}</code></pre><p>This might look fine, but the NotificationService is tightly coupled to EmailSender. It knows exactly how to create it, including the SMTP server and port. What if you need to change the email provider? What if you want to send SMS notifications instead of emails? What if you need to test this class without actually sending emails? You can&#8217;t. You have to modify the NotificationService class every time.</p><p>Here&#8217;s the same class using dependency injection:</p><pre><code>public interface INotificationSender
{
    void Send(string recipient, string message);
}

public class NotificationService
{
    private INotificationSender _sender;
    
    public NotificationService(INotificationSender sender)
    {
        _sender = sender;
    }
    
    public void NotifyUser(string recipient, string message)
    {
        _sender.Send(recipient, message);
    }
}</code></pre><p>Now you can have different implementations, and the NotificationService doesn't know or care which implementation it gets. It just knows it can call Send on whatever is provided.</p><pre><code>public class EmailSender : INotificationSender
{
    private string _smtpServer;
    private int _port;
    
    public EmailSender(string smtpServer, int port)
    {
        _smtpServer = smtpServer;
        _port = port;
    }
    
    public void Send(string email, string message)
    {
        // Send email
    }
}

public class SmsSender : INotificationSender
{
    private string _apiKey;
    
    public SmsSender(string apiKey)
    {
        _apiKey = apiKey;
    }
    
    public void Send(string phoneNumber, string message)
    {
        // Send SMS
    }
}</code></pre><p>When used well, dependency injection reduces the complexity of each class, allowing it to focus on its single responsibility. It may seem like you end up with the same code in a different place, but that is exactly the point. By removing the assembly code from your classes, you make them more independent, reusable, and testable.</p><p>In practice, dependency injection can be summarized as avoiding the new keyword in your classes and demanding instances of your dependencies through the constructor.</p><p>Frameworks like ASP.NET Core have built-in dependency injection containers that automatically create and provide dependencies. But you don&#8217;t need a framework to use dependency injection. You can apply it in any codebase.</p><h2><strong>Inversion of Control</strong></h2><p>Dependency injection is part of a broader principle called Inversion of Control. The first is more specific and examines how things are made and how their dependencies are assembled. Inversion of Control, on the other hand, is more general and can be used to solve a wide range of issues.</p><p>Inversion of Control is a design pattern that removes responsibilities from a class, making it more straightforward and less tightly coupled to the rest of the system. To understand it, think about what it would take to build a web API from scratch in C# without any framework. No ASP.NET Core, no web server, nothing. Just C#.</p><p>You would need to write a lot of code. You have to open network sockets, parse HTTP requests, route them to the appropriate handlers, manage threads, handle errors, log everything, and send responses. You need to control the entire application flow.</p><p>Now consider building the same API using ASP.NET Core. You don&#8217;t write any of that infrastructure code. Instead, you create controllers:</p><pre><code>[ApiController]
[Route("api/payments")]
public class PaymentController : ControllerBase
{
    private readonly IPaymentService _paymentService;
    
    public PaymentController(IPaymentService paymentService)
    {
        _paymentService = paymentService;
    }
    
    [HttpPost]
    public IActionResult ProcessPayment([FromBody] PaymentRequest request)
    {
        var result = _paymentService.Process(request);
        return Ok(result);
    }
}</code></pre><p>The controller doesn&#8217;t know when it will be created, who will call its methods, or how the HTTP request gets to it. The framework handles all of that. Your code becomes a plugin to the framework. </p><p>This is Inversion of Control. You&#8217;re not in control anymore. The framework is. A good Inversion of Control framework has a few key characteristics. You can create plugins or extensions. Each plugin is independent and can be added or removed at any time. The framework can auto-detect these plugins or provide a way to configure which ones should be used. The framework defines interfaces for each plugin type and isn&#8217;t coupled to the plugins themselves. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1bpB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80c0e6fe-4e97-4ff8-9b09-a9c2a3c9d325_1739x1143.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1bpB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80c0e6fe-4e97-4ff8-9b09-a9c2a3c9d325_1739x1143.png 424w, https://substackcdn.com/image/fetch/$s_!1bpB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80c0e6fe-4e97-4ff8-9b09-a9c2a3c9d325_1739x1143.png 848w, https://substackcdn.com/image/fetch/$s_!1bpB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80c0e6fe-4e97-4ff8-9b09-a9c2a3c9d325_1739x1143.png 1272w, https://substackcdn.com/image/fetch/$s_!1bpB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80c0e6fe-4e97-4ff8-9b09-a9c2a3c9d325_1739x1143.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1bpB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80c0e6fe-4e97-4ff8-9b09-a9c2a3c9d325_1739x1143.png" width="484" height="318.1236263736264" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/80c0e6fe-4e97-4ff8-9b09-a9c2a3c9d325_1739x1143.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:957,&quot;width&quot;:1456,&quot;resizeWidth&quot;:484,&quot;bytes&quot;:190690,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/182004674?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80c0e6fe-4e97-4ff8-9b09-a9c2a3c9d325_1739x1143.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1bpB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80c0e6fe-4e97-4ff8-9b09-a9c2a3c9d325_1739x1143.png 424w, https://substackcdn.com/image/fetch/$s_!1bpB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80c0e6fe-4e97-4ff8-9b09-a9c2a3c9d325_1739x1143.png 848w, https://substackcdn.com/image/fetch/$s_!1bpB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80c0e6fe-4e97-4ff8-9b09-a9c2a3c9d325_1739x1143.png 1272w, https://substackcdn.com/image/fetch/$s_!1bpB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80c0e6fe-4e97-4ff8-9b09-a9c2a3c9d325_1739x1143.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Inversion of Control is everywhere in modern frameworks. Once you start looking for it, you&#8217;ll see it in ASP.NET Core, Entity Framework, dependency injection containers, and many other tools you use every day.</p><h2><strong>Bringing It Together</strong></h2><p>The three principles we have discussed in this article work together to help you write better code. The Open-Closed Principle gives you the structure. It shows you how to separate what changes from what stays the same using interfaces and abstractions.</p><p>Dependency Injection gives you the mechanism. It shows you how to give your classes dependencies rather than have them create their own. Inversion of Control gives you the pattern. It shows you how to build systems where your code becomes plugins to a larger framework.</p><p>These principles have been around for years because they work. When you apply these principles, you reduce coupling and complexity, and your code becomes easier to test, extend, and maintain. </p>]]></content:encoded></item><item><title><![CDATA[Treaps: A Hybrid Data Structure]]></title><description><![CDATA[How to Combine Trees and Heaps for Efficient Data Management.]]></description><link>https://newsletter.francofernando.com/p/treaps-a-hybrid-data-structure</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/treaps-a-hybrid-data-structure</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Fri, 16 Jan 2026 10:09:58 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!_NoT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff55b7d63-471c-42bf-84d9-1222310449c7_2174x1882.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends,</p><p>Welcome to the 156th issue of the Polymathic Engineer newsletter.</p><p>This week, we will take a closer look at treaps, a hybrid data structure that beautifully combines the best properties of binary search trees and heaps.</p><p>If you have worked with binary search trees, you know they are great for maintaining sorted data and providing fast lookups. If you have used heaps, you know how well they track the highest- (or lowest) priority element. But what if you need both features in a single structure?</p><p>That&#8217;s precisely where treaps shine. The name itself comes from the words "tree" and "heap," and that's what they are: a hybrid data structure that keeps elements in sorted order while simultaneously tracking priorities.</p><p>The outline will be as follows:</p><ul><li><p>Multi-Dimensional Data Indexing</p></li><li><p>Combining Trees and Heaps</p></li><li><p>How Treaps Work</p></li><li><p>The Balance Problem and Randomization</p></li><li><p>Applications and Use Cases</p></li></ul><h2><strong>Multi-Dimensional Data Indexing</strong></h2><p>Imagine you are building a task management system for a development team. Each task has a unique identifier (e.g., &#8220;bug-1247&#8221; or &#8220;feature-dark-mode&#8221;) and a priority score. The priority score is based on how important the task is to the business, how quickly it needs to be done, and how many other tasks it depends on.</p><p>Your system needs to support two critical operations:</p><ul><li><p>Search for a specific task by its identifier to update details like status, assignee, or description.</p></li><li><p>Retrieve the highest-priority task that should be worked on next.</p></li></ul><p>Both operations need to be fast, because developers use the system all day long.</p><p>The straightforward approach is to use a hash table for the tasks. Hash tables provide O(1) average-case lookups by identifier, which handles the first requirement perfectly. However, it becomes difficult to find the highest-priority job. You would need to scan through all the n tasks to find the one with the maximum priority, and that results in O(n) time complexity.</p><p>Another option is to use a min-heap ordered by priority. You can always get to the top priority task in a heap in O(1) time, but it takes O(log n) time to extract it. The trouble now moves to the search process. Heaps aren&#8217;t meant for quick, key-based searches. To find a specific task by its identifier, you have to scan the whole heap, which takes O(n) time.</p><p>At this point, you might consider using both structures simultaneously: a hash table for fast lookups and a heap for priority tracking. This approach works but comes with several challenges. First, you need twice the memory since each task is stored in both structures. Second, you need to keep both structures synchronized. When you update a task&#8217;s priority, you must update it in both places. When you delete a task, you must remove it from both structures.</p><p>The main problem is that each task has two dimensions: its identifier and its priority. We need a data structure that can efficiently manage operations on both dimensions without the overhead of maintaining two separate structures.</p><p>This is where treaps come in. They natively support fast key-based search and provide quick access to the element with the highest priority. In the following sections, we&#8217;ll see how treaps achieve this by combining the structural properties of trees and heaps in a single data structure.</p><h2><strong>Combining Trees and Heaps</strong></h2><p>Treaps solve the problem of multidimensional sorting by making sure that the same data structure satisfies two distinct constraints for ordering.</p><p>The first constraint comes from binary search trees. Every node in a BST is associated with a key. The keys in its left subtree are smaller, and the keys in its right subtree are larger. This property enables efficient search operations, as you can traverse the tree by comparing keys and selecting the appropriate branch at each node.</p><p>The second constraint comes from heaps. In a min-heap, the priority of any node is no greater than the priorities of its children. This property makes sure that the node with the lowest priority value is always at the root of the tree.</p><p>The key concept is that you can apply these two constraints to different attributes of the same data. In our task management example, the task identifiers follow the BST (horizontal) ordering constraint, while the priority scores follow the heap (vertical) ordering constraint.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_NoT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff55b7d63-471c-42bf-84d9-1222310449c7_2174x1882.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_NoT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff55b7d63-471c-42bf-84d9-1222310449c7_2174x1882.png 424w, https://substackcdn.com/image/fetch/$s_!_NoT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff55b7d63-471c-42bf-84d9-1222310449c7_2174x1882.png 848w, https://substackcdn.com/image/fetch/$s_!_NoT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff55b7d63-471c-42bf-84d9-1222310449c7_2174x1882.png 1272w, https://substackcdn.com/image/fetch/$s_!_NoT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff55b7d63-471c-42bf-84d9-1222310449c7_2174x1882.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_NoT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff55b7d63-471c-42bf-84d9-1222310449c7_2174x1882.png" width="554" height="479.4230769230769" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f55b7d63-471c-42bf-84d9-1222310449c7_2174x1882.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1260,&quot;width&quot;:1456,&quot;resizeWidth&quot;:554,&quot;bytes&quot;:329754,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/182943012?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff55b7d63-471c-42bf-84d9-1222310449c7_2174x1882.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_NoT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff55b7d63-471c-42bf-84d9-1222310449c7_2174x1882.png 424w, https://substackcdn.com/image/fetch/$s_!_NoT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff55b7d63-471c-42bf-84d9-1222310449c7_2174x1882.png 848w, https://substackcdn.com/image/fetch/$s_!_NoT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff55b7d63-471c-42bf-84d9-1222310449c7_2174x1882.png 1272w, https://substackcdn.com/image/fetch/$s_!_NoT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff55b7d63-471c-42bf-84d9-1222310449c7_2174x1882.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Let&#8217;s look at a concrete example to better understand. If we have seven tasks with the following identifiers and priorities: (&#8220;bug-1247&#8221;, 77), (&#8220;feature-auth&#8221;, 10), (&#8220;refactor-db&#8221;, 55), (&#8220;docs-api&#8221;, 95), (&#8220;test-ui&#8221;, 32), (&#8220;clean backlog&#8221;, 76), (&#8220;erp&#8221;, 89), (&#8220;schedule call&#8221;, 56).</p><p>In a treap, these tasks might be organized as follows:</p><ul><li><p>The root contains &#8220;feature-auth&#8221; (priority 10) because it has the highest priority</p></li><li><p>&#8220;bug-1247&#8221;, &#8220;docs-api&#8221;, &#8220;clean backlog&#8221;, and &#8220;erp&#8220; are in the left subtree because they come before &#8220;feature-auth&#8221; alphabetically</p></li><li><p>&#8220;refactor-db&#8221;, &#8220;test-ui&#8221;, and &#8220;schedule call&#8221; are in the right subtree because they come after &#8220;feature-auth.&#8221;</p></li><li><p>Within each subtree, the heap property is maintained for priorities</p></li></ul><p>This structure allows you to search for &#8220;clean-up repo&#8221; by traversing left from the root (following the BST property). At the same time, the root is always where you may find the highest-priority task (following the heap property).</p><p>The hard part is making sure that both constraints are met when adding or removing nodes. You can&#8217;t just use standard BST operations because they might not follow the heap property. In the same way, you can&#8217;t use standard heap operations because they might not follow the BST property.</p><p>The solution is to use a special operation called rotation, which allows you to change the tree structure so the heap property is restored without violating the BST ordering.</p><h2>How Treaps Work</h2><p>Now that we understand what treaps are, let&#8217;s see how they actually work. We&#8217;ll start with the data structure representation, then explore rotations, and finally look at the main operations.</p><h4>Node Structure</h4><p>A treap node needs to store more information than a regular BST node. Here is a basic implementation:</p><pre><code>class Node:
    def __init__(self, key, priority):
        self.key = key
        self.priority = priority
        self.left = None
        self.right = None
        self.parent = None
    
    def is_leaf(self):
        return self.left is None and self.right is None
    
    def is_root(self):
        return self.parent is None
    
    def is_left_child(self):
        return self.parent is not None and self.parent.left == self
    
    def is_right_child(self):
        return self.parent is not None and self.parent.right == self

class Treap:
    def __init__(self):
        self.root = None
    
    def _set_left(self, parent, child):
        """Helper to set left child and update parent reference."""
        parent.left = child
        if child is not None:
            child.parent = parent
    
    def _set_right(self, parent, child):
        """Helper to set right child and update parent reference."""
        parent.right = child
        if child is not None:
            child.parent = parent
</code></pre><p>Each node stores its key, priority, and references to its left, right, and parent children. The parent reference is not strictly necessary, but it makes rotations much easier to implement.</p><h4>The Role of Rotations</h4>
      <p>
          <a href="https://newsletter.francofernando.com/p/treaps-a-hybrid-data-structure">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[From Stateful to Stateless: Building Web Apps That Scale]]></title><description><![CDATA[A Practical Guide to Managing State and Building Scalable Web Applications.]]></description><link>https://newsletter.francofernando.com/p/from-stateful-to-stateless-building</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/from-stateful-to-stateless-building</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Fri, 09 Jan 2026 08:16:20 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!p6U9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc181ba0-ebe9-410b-a349-9a5e9ceb461c_2136x828.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends,</p><p>Welcome to the 155th issue of the Polymathic Engineer newsletter, the first in the new year.</p><p>The front-end layer of a web application is where the battle for scalability is won or lost. Every user interaction, every click, every API call flows through this layer. It is your first line of defense and the component receiving the most traffic.</p><p>The front-end layer has to deal with the most traffic and concurrent requests of any part of the system. One hundred users might be manageable. A thousand users start getting interesting. But what do you do when you need to serve 10,000 users at once? Or one hundred thousand?</p><p>The traditional approach of buying bigger servers hits a wall sooner or later. You can only scale vertically so far before you run out of RAM, CPU cores, or budget. Adding more servers to handle the load is the only way to keep going.</p><p>However, you can&#8217;t simply add more servers if your architecture won&#8217;t allow it. The reason is the state. State is any data that makes one server different from another. It could be session data stored in memory, files saved on local disk, or locks held by a specific process. </p><p>When servers hold state, users are tied to particular servers. This means that Requests can't be sent freely to different servers, servers cannot be cloned or replaced without downtime, and auto-scaling becomes a nightmare.</p><p>The solution seems simple: remove all state from your front-end servers. Make them completely interchangeable clones. But simple doesn&#8217;t mean easy. In this article, we will explore the most common types of state that sneak into web applications and the practical strategies to eliminate them. </p><p>The outline will be as follows: </p><ul><li><p>Stateless vs. Stateful</p></li><li><p>Managing HTTP Sessions: Three Approaches</p></li><li><p>Managing Files: User Content and System-Generated Data</p></li><li><p>Managing Other Types of State</p></li></ul><div><hr></div><p>Project-based learning is the best way to develop technical skills. <a href="https://app.codecrafters.io/join?via=FrancoFernando">CodeCrafters </a>is an excellent platform for tackling exciting projects, such as building your own Redis, Kafka, a <a href="https://app.codecrafters.io/join/dns-server?via=francofernando">DNS server</a>, SQLite, or Git from scratch.</p><p><a href="https://app.codecrafters.io/join?via=FrancoFernando">Sign up, and become a better software engineer</a>.</p><div><hr></div><h2><strong>Stateless vs. Stateful</strong></h2><p>Before talking about specific solutions, we need to understand what makes a service stateless or stateful. The difference is simple but very important.</p><p>A <strong>stateless service</strong> doesn&#8217;t hold any data between requests. It processes each request independently, without relying on information stored from previous requests. All the data needed to handle a request comes from either the request itself or from external storage.</p><p>A <strong>stateful service</strong>, on the other hand, keeps data between requests. Other instances of the same service can&#8217;t see this data because it&#8217;s stored locally, like in memory, on disk, or in local variables.</p><p>The key difference is interchangeability. From the point of view of the client, stateless service instances can be swapped out at any time. The client can send a request to any instance and get the same result. Stateful services instead force clients to stick to the same instance, because that&#8217;s where their data is stored.</p><p>Let&#8217;s use an analogy to make this concrete. Imagine you&#8217;re visiting a coffee shop chain with multiple locations across the city. In this analogy, the chain is your website, each location is a server, and ordering a coffee is making a web request.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!p6U9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc181ba0-ebe9-410b-a349-9a5e9ceb461c_2136x828.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!p6U9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc181ba0-ebe9-410b-a349-9a5e9ceb461c_2136x828.png 424w, https://substackcdn.com/image/fetch/$s_!p6U9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc181ba0-ebe9-410b-a349-9a5e9ceb461c_2136x828.png 848w, https://substackcdn.com/image/fetch/$s_!p6U9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc181ba0-ebe9-410b-a349-9a5e9ceb461c_2136x828.png 1272w, https://substackcdn.com/image/fetch/$s_!p6U9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc181ba0-ebe9-410b-a349-9a5e9ceb461c_2136x828.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!p6U9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc181ba0-ebe9-410b-a349-9a5e9ceb461c_2136x828.png" width="1456" height="564" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fc181ba0-ebe9-410b-a349-9a5e9ceb461c_2136x828.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:564,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:253623,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/181161738?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc181ba0-ebe9-410b-a349-9a5e9ceb461c_2136x828.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!p6U9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc181ba0-ebe9-410b-a349-9a5e9ceb461c_2136x828.png 424w, https://substackcdn.com/image/fetch/$s_!p6U9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc181ba0-ebe9-410b-a349-9a5e9ceb461c_2136x828.png 848w, https://substackcdn.com/image/fetch/$s_!p6U9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc181ba0-ebe9-410b-a349-9a5e9ceb461c_2136x828.png 1272w, https://substackcdn.com/image/fetch/$s_!p6U9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc181ba0-ebe9-410b-a349-9a5e9ceb461c_2136x828.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If the chain coffee shop is stateless,<strong> y</strong>ou walk into any location, place your order, pay, and receive your coffee. You provide all the necessary information with every order: what you want, how you want it done, and how you want to pay. The barista doesn&#8217;t need to know anything about you. You can go somewhere else tomorrow and get the same service. Each transaction is complete and independent.</p><p>Things are different if each shop in the chain keeps a physical loyalty card for each customer that the barista needs to stamp after each purchase. Now you are tied to this specific location. If you visit a different branch of the same shop, they can&#8217;t honor your loyalty card because it is not in their system. You need to return to your original location to use the stamps you&#8217;ve accumulated. The chain coffee shop became stateful.</p><p>The state's implications for scalability are enormous. With stateless servers, you can add or remove servers whenever you want. Traffic can be distributed evenly across all instances. If a server crashes, you just route requests to another server without losing any data, since none was stored there in the first place.</p><p>With stateful servers, everything becomes complicated. There must be ways for users to always get to the right server. It is not easy to get rid of servers because users' data would be lost when they are removed. Users are locked into certain instances, so you can't easily distribute the load.</p><h2><strong>Managing HTTP Sessions</strong></h2><p>Most web applications need to keep track of users across multiple requests. You log in once, and the site needs to remember you as you browse across different pages. When you add items to a shopping cart, they need to stay there.</p><p>However, data exchange on the web relies on HTTP, a stateless protocol. Each request is independent, and HTTP doesn&#8217;t retain any information about previous requests. To work around this, web applications use cookies to create the illusion of a session. </p><p>Here is how it works. When a user first visits a website, the server sends back a small piece of data called a session cookie. The browser stores the cookie and sends it with every request to that site. The server then uses the session ID in the cookie to figure out which requests are from the same user and belong to the same session. Even when multiple users connect from the same IP address, cookies let the web server figure out which requests belong to which user.</p><p>Now, each session has associated data, such as the user ID, preferences, shopping cart contents, or the page the user was last viewing. This session-specific data needs to be available on every subsequent request so the application knows who is logged in and can personalize their experience.</p><p>But where does this session data actually live? If it lives in the memory of a specific web server, the user is stuck with that server. All the requests need to go to the same machine. You can&#8217;t freely distribute traffic and easily add or remove servers, and you have created a stateful system.</p><p>There are three practical approaches to avoid this, each with different tradeoffs:</p><ol><li><p><strong>Put Everything in Cookies. </strong>The most straightforward approach is to store all session data in the cookie itself. Instead of the cookie containing just a session ID, it includes the actual data, encrypted and encoded. When a request comes in, the server reads everything it needs from the cookie. When sending a response, it updates the cookie with any changes. This works beautifully when your session data is small, such as a user ID, a security token, and a few preferences. Overhead is minimal, and you avoid the complexity of managing external storage. The problem comes when you need to store more data. Browsers send cookies with every single request, and if your session data is 5KB, that&#8217;s 5KB added to every request and response. On mobile connections, this adds up quickly. To make things worse, consider that after encryption and encoding, your data grows by about a third.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GW5y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F235199cd-9c3d-4e53-aba4-e31ab8af9f98_1017x892.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GW5y!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F235199cd-9c3d-4e53-aba4-e31ab8af9f98_1017x892.png 424w, https://substackcdn.com/image/fetch/$s_!GW5y!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F235199cd-9c3d-4e53-aba4-e31ab8af9f98_1017x892.png 848w, https://substackcdn.com/image/fetch/$s_!GW5y!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F235199cd-9c3d-4e53-aba4-e31ab8af9f98_1017x892.png 1272w, https://substackcdn.com/image/fetch/$s_!GW5y!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F235199cd-9c3d-4e53-aba4-e31ab8af9f98_1017x892.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GW5y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F235199cd-9c3d-4e53-aba4-e31ab8af9f98_1017x892.png" width="432" height="378.9026548672566" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/235199cd-9c3d-4e53-aba4-e31ab8af9f98_1017x892.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:892,&quot;width&quot;:1017,&quot;resizeWidth&quot;:432,&quot;bytes&quot;:84318,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/181161738?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F235199cd-9c3d-4e53-aba4-e31ab8af9f98_1017x892.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!GW5y!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F235199cd-9c3d-4e53-aba4-e31ab8af9f98_1017x892.png 424w, https://substackcdn.com/image/fetch/$s_!GW5y!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F235199cd-9c3d-4e53-aba4-e31ab8af9f98_1017x892.png 848w, https://substackcdn.com/image/fetch/$s_!GW5y!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F235199cd-9c3d-4e53-aba4-e31ab8af9f98_1017x892.png 1272w, https://substackcdn.com/image/fetch/$s_!GW5y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F235199cd-9c3d-4e53-aba4-e31ab8af9f98_1017x892.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div></li></ol>
      <p>
          <a href="https://newsletter.francofernando.com/p/from-stateful-to-stateless-building">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[End of year wrap: 2025]]></title><description><![CDATA[A personal retrospective on the this year. Plus some thoughts about this newsletter and the most-read articles of the year.]]></description><link>https://newsletter.francofernando.com/p/end-of-year-wrap-2025</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/end-of-year-wrap-2025</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Sat, 27 Dec 2025 10:08:07 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!bFoO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cfef9f3-37d7-4023-91b7-8145a967da1e_4032x3024.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bFoO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cfef9f3-37d7-4023-91b7-8145a967da1e_4032x3024.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bFoO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cfef9f3-37d7-4023-91b7-8145a967da1e_4032x3024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!bFoO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cfef9f3-37d7-4023-91b7-8145a967da1e_4032x3024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!bFoO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cfef9f3-37d7-4023-91b7-8145a967da1e_4032x3024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!bFoO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cfef9f3-37d7-4023-91b7-8145a967da1e_4032x3024.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bFoO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cfef9f3-37d7-4023-91b7-8145a967da1e_4032x3024.jpeg" width="1456" height="1092" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7cfef9f3-37d7-4023-91b7-8145a967da1e_4032x3024.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1092,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2078063,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/182591591?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cfef9f3-37d7-4023-91b7-8145a967da1e_4032x3024.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!bFoO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cfef9f3-37d7-4023-91b7-8145a967da1e_4032x3024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!bFoO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cfef9f3-37d7-4023-91b7-8145a967da1e_4032x3024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!bFoO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cfef9f3-37d7-4023-91b7-8145a967da1e_4032x3024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!bFoO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cfef9f3-37d7-4023-91b7-8145a967da1e_4032x3024.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Hi Friends,</p><p>Welcome to the 154th issue of the Polymathic Engineer newsletter. </p><p>This is the last article of 2025, and the outline will be as follows:</p><ul><li><p>End-of-year reflections</p></li><li><p>The Polymathic Engineer in 2025</p></li><li><p>Best articles of the year</p></li></ul>
      <p>
          <a href="https://newsletter.francofernando.com/p/end-of-year-wrap-2025">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Control Planes and Data Planes ]]></title><description><![CDATA[A not-well-known pattern for designing better distributed systems.]]></description><link>https://newsletter.francofernando.com/p/control-planes-and-data-planes</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/control-planes-and-data-planes</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Sat, 20 Dec 2025 06:30:36 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Ymtg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd96ae614-928d-45f5-b984-936523fb5eae_971x659.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends,</p><p>Welcome to the 153rd issue of the Polymathic Engineer. </p><p>When you design a distributed system, one of the most important aspects is how you divide it into components. What should each part have? How should they talk to each other? It may seem easy to answer these questions correctly, but it is very important that you do. Changing the general ar&#8230;</p>
      <p>
          <a href="https://newsletter.francofernando.com/p/control-planes-and-data-planes">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Fundamental Graph Algorithms Part III: DFS On Directed Graphs]]></title><description><![CDATA[How depth-first search shines on directed graphs: topological sorting and strongly connected component algorithms]]></description><link>https://newsletter.francofernando.com/p/fundamental-graphs-algorithms-part</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/fundamental-graphs-algorithms-part</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Sat, 13 Dec 2025 10:27:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!jPIE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578ad373-0774-413c-b029-bdb304a134fb_1695x1560.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends,</p><p>Welcome to the 152nd issue of the Polymathic Engineer newsletter.</p><p>This week, we will complete our in-depth series of articles about fundamental graph algorithms and their applications. In particular, we will discuss the most critical uses of depth-first search on undirected graphs. The outline will be the following:</p><ul><li><p>What is Topological Sorting</p></li><li><p>How to use DFS for Topological Sorting  </p></li><li><p>Implementation</p></li><li><p>Strongly Connected Components</p></li><li><p>The Two-Pass Algorithm</p></li><li><p>Implementation</p></li><li><p>A concrete example</p></li></ul><div><hr></div><p>Project-based learning is the best way to develop technical skills. <a href="https://app.codecrafters.io/join?via=FrancoFernando">CodeCrafters </a>is an excellent platform for tackling exciting projects, such as building your own Redis, Kafka, a <a href="https://app.codecrafters.io/join/dns-server?via=francofernando">DNS server</a>, SQLite, or Git from scratch.</p><p><a href="https://app.codecrafters.io/join?via=FrancoFernando">Sign up, and become a better software engineer</a>.</p><div><hr></div><h3>What is Topological Sorting</h3><p>Topological sorting is an algorithm that puts the vertices of a directed acyclic graph (DAG) in a linear order such that for every directed edge (u, v), vertex u appears before vertex v in the ordering. If you visualize vertices along a horizontal line, all directed edges point from left to right.</p><p>Looking at this definition, it&#8217;s clear that this order can only work if there are no loops in the graph. Indeed, it's not possible to keep going straight on a line and end up back where you started if there is a loop.</p><p>This sorting operation is critical for scheduling problems. Consider building software in which some modules depend on others, compiling code in which some files need to be processed before others, or taking university courses with prerequisites. In all such cases, you need an ordering that respects the dependencies. That is what a topological sort gives you.</p><p>Still, this is just the tip of the iceberg since topological sorting is a preprocessing step for almost all algorithms operating on DAGs. For example, to find the longest path in a DAG, you can process vertices in topological order. Since you deal with each vertex after all its predecessors, you can compute the longest path to each vertex one step at a time without worrying about missing any incoming edges.</p><h2>How to use DFS for Topological Sorting</h2><p>The most important thing to understand is that DFS automatically generates a reverse topological ordering based on its finishing times. Once you have explored all outgoing edges from a vertex, you can safely add it to the reversed topological order. The reason is that you have already handled everything the vertex depends on.</p><p>Let's consider what may happen to each directed edge (u, v) during a DFS run. There are three possibilities:</p><ol><li><p><strong>If v is undiscovered</strong>, we start a DFS from v before continuing with u. This means v finishes before u, so u appears before v in the reverse order, which is exactly what we want.</p></li><li><p><strong>If v is already discovered</strong> but not yet processed, (u, v) is a back edge, which means the graph has a cycle. This contradicts our assumption that the graph is a DAG.</p></li><li><p><strong>If v is already processed</strong>, it has been processed before u. Therefore, v will be added to the topological order before u, and when we reverse the order, u will come before v, as desired.</p></li></ol><p>Once you understand these three points, the algorithm is simple: perform a DFS on the graph and push each vertex onto a stack when you finish processing it. Popping out the vertices from the stack gives you the topological order. </p><p>Let&#8217;s walk through an example using a graph with vertices (1&#8594;2), (1&#8594;3), (2&#8594;3), (2&#8594;4),(3&#8594;5), (3&#8594;6), (5&#8594;4), (6&#8594;5), (7&#8594;6), (7&#8594;1). This could represent a build system where each vertex is a module and edges represent dependencies (module u must be built before module v).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jPIE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578ad373-0774-413c-b029-bdb304a134fb_1695x1560.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jPIE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578ad373-0774-413c-b029-bdb304a134fb_1695x1560.png 424w, https://substackcdn.com/image/fetch/$s_!jPIE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578ad373-0774-413c-b029-bdb304a134fb_1695x1560.png 848w, https://substackcdn.com/image/fetch/$s_!jPIE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578ad373-0774-413c-b029-bdb304a134fb_1695x1560.png 1272w, https://substackcdn.com/image/fetch/$s_!jPIE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578ad373-0774-413c-b029-bdb304a134fb_1695x1560.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jPIE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578ad373-0774-413c-b029-bdb304a134fb_1695x1560.png" width="310" height="285.3021978021978" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/578ad373-0774-413c-b029-bdb304a134fb_1695x1560.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1340,&quot;width&quot;:1456,&quot;resizeWidth&quot;:310,&quot;bytes&quot;:190411,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/180804145?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578ad373-0774-413c-b029-bdb304a134fb_1695x1560.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!jPIE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578ad373-0774-413c-b029-bdb304a134fb_1695x1560.png 424w, https://substackcdn.com/image/fetch/$s_!jPIE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578ad373-0774-413c-b029-bdb304a134fb_1695x1560.png 848w, https://substackcdn.com/image/fetch/$s_!jPIE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578ad373-0774-413c-b029-bdb304a134fb_1695x1560.png 1272w, https://substackcdn.com/image/fetch/$s_!jPIE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F578ad373-0774-413c-b029-bdb304a134fb_1695x1560.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Starting DFS from vertex 1, this is what happens:</p><ul><li><p>Discover vertex 1, explore its first neighbor 2</p></li><li><p>Discover vertex 2, explore its first neighbor 3</p></li><li><p>Discover vertex 3, explore its first neighbor 5</p></li><li><p>Discover vertex 5, explore its first neighbor 4</p></li><li><p>Discover vertex 4, no outgoing edges (vertex 4 is marked processed and added to the stack)</p></li><li><p>Backtrack to vertex 5 (vertex 5 is marked processed and added to the stack)</p></li><li><p>Backtrack to vertex 3, explore its second neighbor 6</p></li><li><p>Discover vertex 6, which has no more undiscovered neighbors (vertex 6 is marked processed and added to the stack)</p></li><li><p>Backtrack to vertex 3 (vertex 3 is marked processed and added to the stack)</p></li><li><p>Backtrack to vertex 2, which has no more undiscovered neighbors (vertex 2 is marked processed and added to the stack)</p></li><li><p>Backtrack to vertex 1, which has no more undiscovered neighbors (vertex 1 is marked processed and added to the stack)</p></li><li><p>Check for undiscovered vertices, find vertex 7</p></li><li><p>Discover vertex 7, which has no more undiscovered neighbors (vertex 7 is marked processed and added to the stack)</p></li></ul><p>The vertices finish in this order: 4, 5, 6, 3, 2, 1, 7, and the final topological order is: 7, 1, 2, 3, 6, 5, 4. You can verify that this ordering is valid by checking that each edge points from left to right.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wI9V!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2357bdbf-4367-4581-b38c-c026d3aa1da2_3309x788.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wI9V!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2357bdbf-4367-4581-b38c-c026d3aa1da2_3309x788.png 424w, https://substackcdn.com/image/fetch/$s_!wI9V!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2357bdbf-4367-4581-b38c-c026d3aa1da2_3309x788.png 848w, https://substackcdn.com/image/fetch/$s_!wI9V!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2357bdbf-4367-4581-b38c-c026d3aa1da2_3309x788.png 1272w, https://substackcdn.com/image/fetch/$s_!wI9V!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2357bdbf-4367-4581-b38c-c026d3aa1da2_3309x788.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wI9V!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2357bdbf-4367-4581-b38c-c026d3aa1da2_3309x788.png" width="630" height="150.14423076923077" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2357bdbf-4367-4581-b38c-c026d3aa1da2_3309x788.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:347,&quot;width&quot;:1456,&quot;resizeWidth&quot;:630,&quot;bytes&quot;:204343,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/180804145?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2357bdbf-4367-4581-b38c-c026d3aa1da2_3309x788.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!wI9V!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2357bdbf-4367-4581-b38c-c026d3aa1da2_3309x788.png 424w, https://substackcdn.com/image/fetch/$s_!wI9V!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2357bdbf-4367-4581-b38c-c026d3aa1da2_3309x788.png 848w, https://substackcdn.com/image/fetch/$s_!wI9V!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2357bdbf-4367-4581-b38c-c026d3aa1da2_3309x788.png 1272w, https://substackcdn.com/image/fetch/$s_!wI9V!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2357bdbf-4367-4581-b38c-c026d3aa1da2_3309x788.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Implementation</h3><p>Here is a possible implementation of the topological sorting using DFS:</p><pre><code>class TopologicalSort(DFS):
    def __init__(self, graph):
        super().__init__(graph)
        self.sorted_vertices = []
        self.has_cycle = False
    
    def postprocess_vertex(self, vertex):
        # Add vertex to the beginning of the list (equivalent to pushing to stack)
        self.sorted_vertices.insert(0, vertex)
    
    def process_edge(self, from_vertex, to_vertex):
        edge_type = self.edge_classification(from_vertex, to_vertex)
        
        if edge_type == &#8220;BACK&#8221;:
            self.has_cycle = True
            print(f&#8221;Warning: cycle detected at edge ({from_vertex}, {to_vertex})&#8221;)
            print(&#8221;Graph is not a DAG - topological sort not possible&#8221;)
            self.finished = True
    
    def topological_sort(self):
        &#8220;&#8221;&#8220;Perform topological sort on the graph&#8221;&#8220;&#8221;
        self.search_all()
        
        if self.has_cycle:
            return None
        
        return self.sorted_vertices
</code></pre><p>The implementation uses <code>insert(0, vertex)</code> to add vertices to the front of a list, which is equivalent to pushing them onto a stack and then reversing. Alternatively, you could append to the list and reverse it at the end, or use an actual stack and pop elements. The time complexity is O(V + E), dominated by the DFS traversal. Building the topological order adds only a constant amount of work per vertex.</p><p>Finally, an important thing to keep in mind is that a DAG can have multiple correct topological orders, all of which respect the dependencies. You get a different sorting depending on the order in which you look at neighbors in DFS.</p><h2><strong>Strongly Connected Components</strong></h2><p>A strongly connected component (SCC) is a maximal set of vertices in a directed graph where there is a path between every pair of vertices. In other words, you can reach any vertex from any other vertex within the same component by following directed edges.</p>
      <p>
          <a href="https://newsletter.francofernando.com/p/fundamental-graphs-algorithms-part">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Fundamental Graph Algorithms - Part II: DFS]]></title><description><![CDATA[Depth First Search and its applications on undirected graphs: finding cycles and articulation vertices.]]></description><link>https://newsletter.francofernando.com/p/fundamental-graph-algorithms-part-893</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/fundamental-graph-algorithms-part-893</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Sat, 06 Dec 2025 06:31:09 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!P2_t!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27487545-ce10-4172-bc63-c55c24414130_2037x1345.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends,</p><p>Welcome to the 151st issue of the Polymathic Engineer. </p><p>This week, we&#8217;ll continue our discussion about fundamental graph algorithms. While we discussed Breadth-first search in the last issue, this article covers DFS. Depth-first search takes a different approach to graph traversal. </p><p>Instead of looking at neighbors level by level like BFS, DFS goes as deep as possible along each path before backtracking. If BFS is like ripples spreading across a pond, DFS is more like going through a maze by always taking the first unexplored path.</p><p>The outline will be as follows:</p><ul><li><p>Basic concepts</p></li><li><p>Key Properties</p></li><li><p>Implementation</p></li><li><p>Cycle detection</p></li><li><p>Articulation vertices</p></li></ul><div><hr></div><p>Project-based learning is the best way to develop technical skills. <a href="https://app.codecrafters.io/join?via=FrancoFernando">CodeCrafters </a>is an excellent platform for tackling exciting projects, such as building your own Redis, Kafka, a <a href="https://app.codecrafters.io/join/dns-server?via=francofernando">DNS server</a>, SQLite, or Git from scratch.</p><p><a href="https://app.codecrafters.io/join?via=FrancoFernando">Sign up, and become a better software engineer</a>.</p><div><hr></div><h3><strong>Basic Concepts</strong></h3><p>The main difference between BFS and DFS is the data structure used to keep track of the discovered, but not yet processed, vertices. BFS uses a queue, while DFS utilizes a stack. However, the beauty of DFS is that you don&#8217;t need to use an explicit stack, but you can leverage recursion to get a simple yet elegant implementation.</p><p>The algorithm is straightforward. You start by choosing an initial vertex, and then you repeat these steps:</p><ul><li><p>Mark the current vertex as discovered</p></li><li><p>Process the vertex if needed</p></li><li><p>For each neighbor of the current vertex:</p><ul><li><p>If the neighbor is undiscovered, recursively visit it</p></li></ul></li><li><p>Mark the current vertex as processed</p></li></ul><p>The recursive nature of DFS means you explore each path completely before moving to the next one. When you discover a new vertex, you immediately explore all vertices reachable from it before returning to examine other neighbors of the original vertex.</p><p>Let&#8217;s walk through an example using the graph with vertices (1-2), (1-7), (1-8), (2-3), (2-5), (3-4), (3-5), (4-5), (5-6).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!P2_t!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27487545-ce10-4172-bc63-c55c24414130_2037x1345.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!P2_t!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27487545-ce10-4172-bc63-c55c24414130_2037x1345.png 424w, https://substackcdn.com/image/fetch/$s_!P2_t!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27487545-ce10-4172-bc63-c55c24414130_2037x1345.png 848w, https://substackcdn.com/image/fetch/$s_!P2_t!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27487545-ce10-4172-bc63-c55c24414130_2037x1345.png 1272w, https://substackcdn.com/image/fetch/$s_!P2_t!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27487545-ce10-4172-bc63-c55c24414130_2037x1345.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!P2_t!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27487545-ce10-4172-bc63-c55c24414130_2037x1345.png" width="436" height="287.77197802197804" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/27487545-ce10-4172-bc63-c55c24414130_2037x1345.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:961,&quot;width&quot;:1456,&quot;resizeWidth&quot;:436,&quot;bytes&quot;:185405,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/177905554?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27487545-ce10-4172-bc63-c55c24414130_2037x1345.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!P2_t!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27487545-ce10-4172-bc63-c55c24414130_2037x1345.png 424w, https://substackcdn.com/image/fetch/$s_!P2_t!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27487545-ce10-4172-bc63-c55c24414130_2037x1345.png 848w, https://substackcdn.com/image/fetch/$s_!P2_t!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27487545-ce10-4172-bc63-c55c24414130_2037x1345.png 1272w, https://substackcdn.com/image/fetch/$s_!P2_t!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27487545-ce10-4172-bc63-c55c24414130_2037x1345.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>Starting from vertex 1:</p><ul><li><p>Discover 1, explore its first neighbor 2</p></li><li><p>Discover 2, explore its first neighbor 3</p></li><li><p>Discover 3, explore its first neighbor 4</p></li><li><p>Discover 4, explore its first neighbor 5</p></li><li><p>Discover 5, explore its first neighbor 6</p></li><li><p>Discover 6, all neighbors already discovered, backtrack to 5</p></li><li><p>At 5, all neighbors discovered, backtrack to 4</p></li><li><p>At 4, all neighbors discovered, backtrack to 3</p></li><li><p>At 3, all neighbors discovered, backtrack to 2</p></li><li><p>At 2, discover the remaining neighbor 7</p></li><li><p>Discover 7, all neighbors already discovered, backtrack to 2</p></li><li><p>At 2, all neighbors discovered, backtrack to 1</p></li><li><p>At 1, discover the remaining neighbor 8</p></li><li><p>Discover 8, all neighbors already discovered, backtrack to 1</p></li><li><p>At 1, all neighbors discovered, done</p></li></ul><p>The discovery order is 1, 2, 3, 4, 5, 6, 7, 8, which is very different from what BFS would produce. BFS would discover vertices in order of their distance from vertex 1, visiting 2, 7, and 8 before moving to 3, while DFS went as deep as possible before exploring other branches. </p><p>Similar to BFS, DFS builds a tree data structure as it explores the graph. Each time you discover a new vertex, you can record which vertex discovered it using a parent pointer. This creates a tree rooted at the starting vertex. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kRJf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba3b3265-f00f-489e-bf3f-d55b8506687a_1865x2306.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kRJf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba3b3265-f00f-489e-bf3f-d55b8506687a_1865x2306.png 424w, https://substackcdn.com/image/fetch/$s_!kRJf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba3b3265-f00f-489e-bf3f-d55b8506687a_1865x2306.png 848w, https://substackcdn.com/image/fetch/$s_!kRJf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba3b3265-f00f-489e-bf3f-d55b8506687a_1865x2306.png 1272w, https://substackcdn.com/image/fetch/$s_!kRJf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba3b3265-f00f-489e-bf3f-d55b8506687a_1865x2306.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kRJf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba3b3265-f00f-489e-bf3f-d55b8506687a_1865x2306.png" width="306" height="378.2967032967033" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ba3b3265-f00f-489e-bf3f-d55b8506687a_1865x2306.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1800,&quot;width&quot;:1456,&quot;resizeWidth&quot;:306,&quot;bytes&quot;:261567,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/177905554?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba3b3265-f00f-489e-bf3f-d55b8506687a_1865x2306.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kRJf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba3b3265-f00f-489e-bf3f-d55b8506687a_1865x2306.png 424w, https://substackcdn.com/image/fetch/$s_!kRJf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba3b3265-f00f-489e-bf3f-d55b8506687a_1865x2306.png 848w, https://substackcdn.com/image/fetch/$s_!kRJf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba3b3265-f00f-489e-bf3f-d55b8506687a_1865x2306.png 1272w, https://substackcdn.com/image/fetch/$s_!kRJf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba3b3265-f00f-489e-bf3f-d55b8506687a_1865x2306.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>One subtle but important point: the order in which you examine neighbors matters. Different orderings can produce different DFS trees and different edge classifications, though tree and back edges remain consistent (the graph has the same shape). This is typically not a problem in practice because most DFS-based algorithms work correctly regardless of the specific ordering.</p><h3><strong>Key Properties of DFS</strong></h3><p>There are two properties of DFS worth knowing, as they provide valuable information about the graph&#8217;s structure: <strong>timestamps</strong> and <strong>edge classification</strong>. </p><p>DFS maintains two important timestamps for each vertex: the discovery time (when you visit it for the first time and mark it as discovered) and the finishing time (when you have explored all its edges and marked it as processed). The time is initialized to zero and incremented by one every time one of these events occurs.</p><p>These timestamps follow a parenthesis structure. If you represent the discovery time of a vertex as an opening parenthesis and the finishing time as a closing parenthesis, the parentheses are properly nested. This property is helpful because it allows you to determine ancestor-descendant relationships simply by comparing timestamps. The descendants of a vertex u have timestamps that are nested within u's timestamp. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SVBw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecaec9cf-b7f1-4df8-8e1b-42c0235df4bc_1171x578.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SVBw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecaec9cf-b7f1-4df8-8e1b-42c0235df4bc_1171x578.png 424w, https://substackcdn.com/image/fetch/$s_!SVBw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecaec9cf-b7f1-4df8-8e1b-42c0235df4bc_1171x578.png 848w, https://substackcdn.com/image/fetch/$s_!SVBw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecaec9cf-b7f1-4df8-8e1b-42c0235df4bc_1171x578.png 1272w, https://substackcdn.com/image/fetch/$s_!SVBw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecaec9cf-b7f1-4df8-8e1b-42c0235df4bc_1171x578.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SVBw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecaec9cf-b7f1-4df8-8e1b-42c0235df4bc_1171x578.png" width="566" height="279.37489325362935" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ecaec9cf-b7f1-4df8-8e1b-42c0235df4bc_1171x578.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:578,&quot;width&quot;:1171,&quot;resizeWidth&quot;:566,&quot;bytes&quot;:79168,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/177905554?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecaec9cf-b7f1-4df8-8e1b-42c0235df4bc_1171x578.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!SVBw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecaec9cf-b7f1-4df8-8e1b-42c0235df4bc_1171x578.png 424w, https://substackcdn.com/image/fetch/$s_!SVBw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecaec9cf-b7f1-4df8-8e1b-42c0235df4bc_1171x578.png 848w, https://substackcdn.com/image/fetch/$s_!SVBw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecaec9cf-b7f1-4df8-8e1b-42c0235df4bc_1171x578.png 1272w, https://substackcdn.com/image/fetch/$s_!SVBw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecaec9cf-b7f1-4df8-8e1b-42c0235df4bc_1171x578.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Indeed, for any two vertices u and v, one of three things must be true:</p><ul><li><p>Their time intervals are completely separate, and neither is an ancestor of the other in the DFS tree</p></li><li><p>u&#8217;s interval is contained within v&#8217;s interval, so u is a descendant of v</p></li><li><p>v&#8217;s interval is contained within u&#8217;s interval, so v is a descendant of u</p></li></ul><p>In addition, the difference between a vertex&#8217;s finishing and discovery times tells you how many descendants it has. Since the clock ticks on each entry and exit, half this difference gives the descendant count.</p><p>The second interesting aspect of DFS is how it classifies edges. The type of each edge gives essential information about a graph and will be used in the algorithms discussed in the following sections. The classification depends on the type of graph.</p>
      <p>
          <a href="https://newsletter.francofernando.com/p/fundamental-graph-algorithms-part-893">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Fundamental Graph Algorithms - Part I: BFS]]></title><description><![CDATA[Breadth First Search and its applications: shortest paths, connected components and bipartite testing.]]></description><link>https://newsletter.francofernando.com/p/fundamental-graph-algorithms-part</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/fundamental-graph-algorithms-part</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Sat, 29 Nov 2025 08:29:08 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!rOsP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d55e550-c824-450e-b121-8f539b2fe0cf_2061x1125.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends,</p><p>Welcome to the 150th issue of the Polymathic Engineer newsletter. </p><p>This week, we start a small series of in-depth articles regarding graph algorithms and their applications. In this first article, we will focus on the basic concepts and the breadth-first search algorithm. The outline will be the following:</p><ul><li><p>Graph traversal</p></li><li><p>Core Concepts</p></li><li><p>Breadth-First Search (BFS)</p></li><li><p>BFS implementation</p></li><li><p>Connected Components</p></li><li><p>Strongly vs. Weekly Connected Components</p></li><li><p>Bipartite Testing</p></li></ul><div><hr></div><p>Tech interviews are hard. But with the right strategy and resources, it&#8217;s a game you can win. <a href="https://bytebytego.com/?ref=fernando26">ByteByteGo</a> provides a set of practical resources that covers all phases of a technical interview. You can find useful content to ace coding, system design, ML, behavioural, and object-oriented interviews, and much more. </p><p><a href="https://bytebytego.com/?ref=fernando26">Join the ByteByteGo platform and start your preparation today </a></p><div><hr></div><h2>Graph traversal</h2><p>Imagine you get lost in a maze. You need to discover the way out, but you don&#8217;t have a map. What strategy would you use? You might choose a direction and go as far as you can along it before going back, or you could go through all the nearby paths in order before going any further. These ideas are at the heart of Depth-First Search (DFS) and Breadth-First Search (BFS), the two most basic graph traversal methods.</p><p>Graph traversal is the methodical process of going to each vertex and edge in a graph. This may seem like a simple job, but it is the basis for many algorithms you use every day. Some examples are detecting circular dependencies in your build system, finding connected components in a social network to identify communities, or figuring out the order to run tasks when some need to finish before others. The solution to all of these problems reduces to using the right traversal algorithm.</p><p>The main challenge is easy to understand: how do you visit every section of the graph without getting stuck in an infinite loop? In a real maze, you could use chalk to mark walls and avoid doing the same steps over and over again. In a graph algorithm, you use flags to keep track of which vertices you&#8217;ve already seen.</p><p>The two fundamental approaches to graph traversal differ in how they decide which path to explore next. Breadth-first search uses a queue to examine vertices in order of their distance from the starting point. Depth-first search uses a stack (or recursion) to explore the graph as deeply as possible before backtracking. None of them is better, but they both do their best in different situations.</p><p>In this series of articles, we&#8217;ll discuss both algorithms in depth, learn about their key properties, and see how they help solve common graph problems. By the end, you will not only know how these algorithms work, but also when and why to utilize each one.</p><h2>Core Concepts</h2><p>Before we go into the details of BFS and DFS, we need to understand a few basic ideas that are true for both. The first is the&nbsp;states of the vertices.</p><p>During traversal, each vertex exists in one of three states: undiscovered, discovered, or processed. At first, each vertex starts as undiscovered. It gets discovered when we first come across it during the traversal. Finally, it becomes processed after we have looked at all its outgoing edges.</p><p>This progression is important because it avoids infinite loops. When we mark a vertex as discovered, we can ignore any edge that is directed to it because the destination has already been put in the list of the vertices to be processed. Similarly, we can ignore an edge that goes to a processed vertex, because it will not tell us anything new about the graph. </p><p>Consider a simple triangle graph with vertices A, B, and C. If we start at A and move to B, then to C, we could return to A and repeat forever. By marking vertices, we break this cycle. When we return to A from C, we see it&#8217;s already been discovered and stop.</p><p>The second thing you need to learn is how to keep track of the discovered but not yet processed vertices. Both BFS and DFS need a data structure to store such vertices, and the choice of the data structure determines the traversal order:</p><p>- BFS uses a <strong>queue</strong> (first-in, first-out), which makes it explore vertices in order of their distance from the starting point</p><p>- DFS uses a <strong>stack</strong> (last-in, first-out), which makes it go deep before looking sideways</p><p>This is the only big difference between the two methods. Changing the data structure changes the way the whole traversal works. In terms of Big O, both algorithms run in O(V + E) time, where V is the number of vertices and E is the number of edges. This is optimal because you need to look at each vertex and edge at least once to explore the graph fully.</p><p>Last but not least, you should know that the way you store a graph doesn&#8217;t affect the correctness and efficiency of traversal algorithms. As we have discussed in one of the previous issues, different kinds of data structures can be used to represent a graph in memory. The two most common are the adjacency matrix and the adjacency list.</p><p><strong>Adjacency matrices</strong> use a 2D array where matrix[i][j] indicates whether an edge exists from vertex i to vertex j. This lets you look up edges in O(1) time but takes O(V&#178;) space, regardless of the number of edges. This works well for dense graphs or when you need to quickly check if an edge exists. <strong>Adjacency lists</strong> store the neighbors of each vertex in a list. This saves memory for sparse graphs and makes it simple to iterate over all the neighbors of a vertex. Most real-world graphs are sparse, so this is usually the best choice.</p><p>For the algorithms we discuss, we will use an adjacency list representation because it is more common and works better for most situations.</p><pre><code><code>class Graph:
    def __init__(self, num_vertices, directed=False):
        self.num_vertices = num_vertices
        self.directed = directed
        self.adj_list = [[] for _ in range(num_vertices + 1)]
    
    def add_edge(self, u, v):
        self.adj_list[u].append(v)
        if not self.directed:
            self.adj_list[v].append(u)
    
    def neighbors(self, vertex):
        return self.adj_list[vertex]</code></code></pre><h2><strong>Breadth-First Search (BFS)</strong></h2><p>Breadth-first search explores a graph by examining all vertices at distance k from the starting point before moving to the vertices at distance k+1. If you imagine dropping a stone in a pond, BFS is like the ripples spreading outward in concentric circles.</p><p>The algorithm is straightforward. You start by marking the initial vertex as discovered and putting it in a queue. Then you repeat these steps until the queue is empty:</p><ul><li><p>Remove a vertex from the front of the queue</p></li><li><p>Examine all its neighboring vertices</p></li><li><p>For each undiscovered neighbor, mark it as discovered and add it to the queue</p></li><li><p>Mark the vertex as processed</p></li></ul><p>The queue is very important here. Because you process vertices in FIFO order, you are guaranteed to explore all vertices at distance 1 before any vertex at distance 2, vertices at distance 2 before any at distance 3, and so on.</p><p>Let&#8217;s walk through an example to solidify our understanding. Consider an undirected graph with six nodes and the following edges: (1-2), (1-5), (1-6), (2, 3), (2, 5), (3, 4), (4, 5).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rOsP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d55e550-c824-450e-b121-8f539b2fe0cf_2061x1125.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rOsP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d55e550-c824-450e-b121-8f539b2fe0cf_2061x1125.png 424w, https://substackcdn.com/image/fetch/$s_!rOsP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d55e550-c824-450e-b121-8f539b2fe0cf_2061x1125.png 848w, https://substackcdn.com/image/fetch/$s_!rOsP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d55e550-c824-450e-b121-8f539b2fe0cf_2061x1125.png 1272w, https://substackcdn.com/image/fetch/$s_!rOsP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d55e550-c824-450e-b121-8f539b2fe0cf_2061x1125.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rOsP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d55e550-c824-450e-b121-8f539b2fe0cf_2061x1125.png" width="574" height="313.41346153846155" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1d55e550-c824-450e-b121-8f539b2fe0cf_2061x1125.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:795,&quot;width&quot;:1456,&quot;resizeWidth&quot;:574,&quot;bytes&quot;:143178,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/176565730?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d55e550-c824-450e-b121-8f539b2fe0cf_2061x1125.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!rOsP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d55e550-c824-450e-b121-8f539b2fe0cf_2061x1125.png 424w, https://substackcdn.com/image/fetch/$s_!rOsP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d55e550-c824-450e-b121-8f539b2fe0cf_2061x1125.png 848w, https://substackcdn.com/image/fetch/$s_!rOsP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d55e550-c824-450e-b121-8f539b2fe0cf_2061x1125.png 1272w, https://substackcdn.com/image/fetch/$s_!rOsP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d55e550-c824-450e-b121-8f539b2fe0cf_2061x1125.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here is what happens after each iteration:</p><ul><li><p>Initially: queue = [1], Processed: []</p></li><li><p>Iteration 1: discovered 2, 5, 6 queue: [2, 5, 6] Processed: [1]</p></li><li><p>Iteration 2: discovered 3, queue = [5, 6, 3] Processed: [1,2]</p></li><li><p>Iteration 3: discover 4, queue = [6, 3, 4] Processed: [1, 2, 5]</p></li><li><p>Iteration 4: queue = [3, 4] Processed: [1, 2, 5, 6]</p></li><li><p>Iteration 5: queue = [4] Processed: [1, 2, 3, 5, 6]</p></li><li><p>Iteration 6: queue = [] Processed: [1, 2, 3, 4, 5, 6]</p></li><li><p>Done</p></li></ul><p>The order in which BFS discovers the vertices is 2, 5, 6, 3, 4, in order of how far they are from the initial vertex 1.</p>
      <p>
          <a href="https://newsletter.francofernando.com/p/fundamental-graph-algorithms-part">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[How the Operating System Manages the Hardware]]></title><description><![CDATA[A Complete Guide to understand what happens when a program runs on your computer.]]></description><link>https://newsletter.francofernando.com/p/how-the-operating-system-manage-the</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/how-the-operating-system-manage-the</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Fri, 21 Nov 2025 08:35:26 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!w3ml!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc4699ff-f151-4bbf-aeb9-96971b1b4079_1301x749.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends,</p><p>Welcome to the 149th issue of the Polymathic Engineer newsletter.</p><p>When you run a program on your laptop, a lot of things happen in the background. Even a simple task, such as printing "Hello, World!" to the screen, requires significant effort from both hardware and software. Processors, memory systems, storage devices, and the operating system that controls them work together to make it possible.</p><p>The raw computing power comes from the hardware. Processors execute instructions, memory stores data and programs, and different I/O devices let computers talk to the outside world.</p><p>Instead, the operating system hides the complexity of the hardware beneath beautiful abstractions. It utilizes concepts such as processes, virtual memory, and files to turn a complex collection of hardware into a base for running programs.</p><p>As a developer, it is important that you know how the hardware is organized and how the operating system interacts with these components.</p><p>You don't have to learn this just for school, but because it has a significant impact on your work. It changes the way you write code, solves performance issues, and enables you to make informed architectural choices.</p><p>In this article, we will explore how hardware parts are organized and interconnected, and examine the fundamental abstractions that operating systems provide. By the end, you will have a good understanding of what happens from the time you press "Enter" on the "Hello, World!" program until the text shows up on your screen.</p><p>The outline will be as follows:</p><ul><li><p>Hardware Components and Organization</p></li><li><p>The Memory Hierarchy and Performance</p></li><li><p>Operating System Abstractions</p></li><li><p>Process Management</p></li><li><p>Virtual Memory Management</p></li><li><p>File System Abstraction</p></li></ul><div><hr></div><p>Project-based learning is the best way to develop technical skills. <a href="https://app.codecrafters.io/join?via=FrancoFernando">CodeCrafters </a>is an excellent platform for practicing exciting projects, such as building your version of Redis, Kafka, <a href="https://app.codecrafters.io/join/dns-server?via=francofernando">DNS server</a>, SQLite, or Git from scratch.</p><p><a href="https://app.codecrafters.io/join?via=FrancoFernando">Sign up, and become a better software engineer</a>.</p><div><hr></div><h2>Hardware Components and Organization</h2><p>A computer system consists of several hardware components that work together. Even if there are many computer systems, they all have a similar look and feel and share the same core parts. Here are the main ones:</p><ol><li><p><strong>System buses</strong> are like highways in your computer. They use electrical impulses to transmit information from one part to another. Information travels in segments called "words." Most computers today use either 4-byte words (32-bit systems) or 8-byte words (64-bit systems). The word size is a critical parameter, as it affects the amount of data the computer can handle at once.</p></li><li><p><strong>I/O </strong>devices connect your computer to the outside world. Some common examples are a keyboard and a mouse for input, a display screen for output, and disk drives for storing data and programs. Each I/O device connects to the system through either a controller or an adapter. Controllers are built into the motherboard. Adapters are separate cards that plug into slots. Both do the same job: they move data between the I/O bus and the device.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!w3ml!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc4699ff-f151-4bbf-aeb9-96971b1b4079_1301x749.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!w3ml!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc4699ff-f151-4bbf-aeb9-96971b1b4079_1301x749.png 424w, https://substackcdn.com/image/fetch/$s_!w3ml!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc4699ff-f151-4bbf-aeb9-96971b1b4079_1301x749.png 848w, https://substackcdn.com/image/fetch/$s_!w3ml!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc4699ff-f151-4bbf-aeb9-96971b1b4079_1301x749.png 1272w, https://substackcdn.com/image/fetch/$s_!w3ml!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc4699ff-f151-4bbf-aeb9-96971b1b4079_1301x749.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!w3ml!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc4699ff-f151-4bbf-aeb9-96971b1b4079_1301x749.png" width="582" height="335.0637970791699" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fc4699ff-f151-4bbf-aeb9-96971b1b4079_1301x749.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:749,&quot;width&quot;:1301,&quot;resizeWidth&quot;:582,&quot;bytes&quot;:414577,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.francofernando.com/i/172914509?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc4699ff-f151-4bbf-aeb9-96971b1b4079_1301x749.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!w3ml!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc4699ff-f151-4bbf-aeb9-96971b1b4079_1301x749.png 424w, https://substackcdn.com/image/fetch/$s_!w3ml!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc4699ff-f151-4bbf-aeb9-96971b1b4079_1301x749.png 848w, https://substackcdn.com/image/fetch/$s_!w3ml!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc4699ff-f151-4bbf-aeb9-96971b1b4079_1301x749.png 1272w, https://substackcdn.com/image/fetch/$s_!w3ml!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc4699ff-f151-4bbf-aeb9-96971b1b4079_1301x749.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div></li><li><p><strong>Main memory</strong> is where your computer stores programs and data while they're being used. It is a temporary storage that gets cleared when you shut down the machine. From a physical point of view, main memory is made of DRAM chips. From a programmer's view, it works like a huge array of bytes. Every byte has its own address, which starts at 0. In general, machine instructions that make up a program can have a variable number of bytes. Data types take up different amounts of space. On a typical 64-bit Linux system, short integers take 2 bytes, integers and floats 4 bytes, long integers and doubles take 8 bytes.</p></li><li><p>The <strong>processor</strong> is the brain of your computer. It reads instructions from memory and executes them one by one. One of the most important parts of a processor is the program counter (PC), which is a word-sized storage device or register. At any point in time, it contains the address of the next machine-language instruction in main memory to execute. The processor follows a simple cycle: it reads an instruction from memory, figures out what the bits in the instruction mean, does a simple task that the instruction tells it to do, and then moves on to the next instruction. There are only a limited number of simple operations, and they all involve the main memory, registers, and the arithmetic/logic unit (ALU). As we've seen with the PC, registers are small memory units that can store words. Each one has its own name. The ALU computes new data and address values. Simple operations that the CPU may perform in response to an instruction include copying data from memory to a register (load), copying data from a register to memory (store), operating on data in registers (doing math or logic on it), or jumping to the next instruction.</p></li></ol><h2>The Memory Hierarchy and Performance</h2><p>As we have seen in the previous paragraph, computer systems use a variety of storage devices that have distinct speeds and costs. This is what software engineers refer to as the processor-memory gap.</p><p>Larger storage devices are generally slower than smaller ones, and faster devices tend to be more expensive than slower ones. The differences are enormous.</p><p>Consider a typical system. Even though a disk drive has 1,000 times more space than main memory, it takes 10,000,000 times longer for the processor to read a word from disk than from memory. That is a huge change in speed.</p>
      <p>
          <a href="https://newsletter.francofernando.com/p/how-the-operating-system-manage-the">
              Read more
          </a>
      </p>
   ]]></content:encoded></item></channel></rss>