<?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>Mon, 04 May 2026 22:57:27 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[How to Stop Failures from Spreading Between Services]]></title><description><![CDATA[Practical patterns to protect your services from failing dependencies and excessive load.]]></description><link>https://newsletter.francofernando.com/p/how-to-stop-failures-from-spreading</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/how-to-stop-failures-from-spreading</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Sat, 02 May 2026 08:43:03 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!r9Su!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0314d5d9-4c7b-4712-8619-7de2b23538db_950x306.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends,</p><p>Welcome to the 171st issue of the Polymathic Engineer newsletter.</p><p>In one of our previous articles, we looked at the most <a href="https://newsletter.francofernando.com/p/why-distributed-systems-fail-and">common root causes of failures</a> in distributed systems and how to use redundancy and fault isolation to contain them. These are protections at the architectural level, which means they work based on how you design and deploy your system.</p><p>In this article, we are going to talk about something more tactical. We will discuss the patterns that stop faults from spreading from one service to another at runtime. These are techniques you can apply to existing systems with little effort.</p><p>The article is split into two parts. The first covers patterns that protect a service when one of its dependencies fails or slows down (downstream resiliency mechanisms). The second covers patterns that protect a service when its callers send more traffic than it can handle (upstream resiliency mechanisms).</p><p>The outline is as follows:</p><ul><li><p>Timeouts</p></li><li><p>Retries</p></li><li><p>Circuit breakers</p></li><li><p>Load shedding</p></li><li><p>Load leveling</p></li><li><p>Rate limiting</p></li><li><p>Constant work</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, HTTP server or Git from scratch using your favourite programming language. Noe you can also try to build your own Claude Code (for free, still in beta).</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>Timeouts</h2><p>When one service makes a network call, it should always set a timeout. The call fails if no response comes back within a certain amount of time. Without a timeout, the call might never return, and as we already discussed in the previous article, network calls that don&#8217;t return lead to resource leaks. Timeouts are the first protection you can use to find connectivity faults and stop them from cascading from one service to another.</p><p>You might think that setting timeouts is such a simple task that all network libraries do it for you, but that's not the case. When JavaScript's fetch API came out, there was no way to specify a timeout. Go's HTTP package doesn't employ timeouts by default, and the default timeout for the most popular Python library is infinity. Modern HTTP clients for Java and .NET do a better job and often come with default timeouts, but it's still a good idea to set them manually every time.  </p><p>There is a golden rule here: always set timeouts when making network calls, and keep a close eye on third-party libraries that make network calls but don&#8217;t let you configure a timeout. But how do you pick a good timeout value? </p><p>One method is to base it on how many false timeouts you are okay with. For example, if you don&#8217;t mind 0.1% of requests timing out even though they would have worked in the end, you can set the timeout according to the 99.9th percentile of the downstream service&#8217;s response time.</p><p>It is also important to have good monitoring in place to measure the full lifecycle of a network call. This allows you to check how long a network call took, what status code came back, and if it timed out. Without this kind of visibility at the points where your system is integrated, it's much harder to figure out what's wrong with production.</p><p>To avoid repeating the same timeout and monitoring logic everywhere, you can wrap the network call in a library that does both. Another option is a sidecar proxy running on the same machine. This proxy will intercept remote calls and take care of timeouts and monitoring for you.</p><h2>Retries</h2><p>When a network call fails or times out, the caller can either give up or try again. If the failure was caused by a short-lived connectivity issue, retrying after a short wait has a good chance of working. However, retrying immediately will make things worse if the downstream service is already too busy, </p><p>This is why retries should be slowed down. The most frequent strategy is exponential backoff, in which the delay between retries increases with each attempt. For example, if the initial wait is 2 seconds and you double it each time with an 8-second cap, the delays will be 2, 4, 8, 8, 8... seconds.</p><p>This is why retries need to be slowed down. The most common method is <strong>exponential backoff</strong>, in which the delay between retries increases with each attempt. For example, if the initial wait is 2 seconds and you double it each time with a cap at 8 seconds, the delays would be 2, 4, 8, 8, 8... seconds.</p><p>Exponential backoff is helpful, but it has a problem. When a downstream service goes down temporarily, many callers&#8217; requests will fail at the same time. They all retry on a similar schedule, which will cause the downstream service to experience load spikes that make things worse. To fix this, you can add some random jitter to the delay. This spreads out the retries over time and reduces the load on the struggling service.</p><p>Actively retrying right away isn&#8217;t always the only option, either. In batch systems that don&#8217;t need an immediate response, a failed request can be put into a retry queue. The same process, or a different one, can pick it up later and try again.</p><p>Additionally, not every failure is worth trying again. If the error isn&#8217;t temporary, such as the caller not being allowed to access the destination, retrying will fail again. If that is the case, the service should stop retrying right away. Also, if the network call is not idempotent, retrying can cause problems that affect the correctness of the application.</p><p>A last problem that is easy to overlook is <strong>retries amplification</strong>. Let&#8217;s suppose you have a chain of three services: the user calls service A, A calls service B, and B calls another service C. If the call from B to C fails, service B retries. But in the meantime, A sees a longer response time. If A times out, it retries too, and this adds even more load to the chain. If the user&#8217;s client also has retries, the total number of requests goes up quickly.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!r9Su!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0314d5d9-4c7b-4712-8619-7de2b23538db_950x306.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!r9Su!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0314d5d9-4c7b-4712-8619-7de2b23538db_950x306.png 424w, https://substackcdn.com/image/fetch/$s_!r9Su!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0314d5d9-4c7b-4712-8619-7de2b23538db_950x306.png 848w, https://substackcdn.com/image/fetch/$s_!r9Su!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0314d5d9-4c7b-4712-8619-7de2b23538db_950x306.png 1272w, https://substackcdn.com/image/fetch/$s_!r9Su!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0314d5d9-4c7b-4712-8619-7de2b23538db_950x306.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!r9Su!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0314d5d9-4c7b-4712-8619-7de2b23538db_950x306.png" width="589" height="189.72" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0314d5d9-4c7b-4712-8619-7de2b23538db_950x306.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:306,&quot;width&quot;:950,&quot;resizeWidth&quot;:589,&quot;bytes&quot;:210002,&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/192933767?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0314d5d9-4c7b-4712-8619-7de2b23538db_950x306.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_!r9Su!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0314d5d9-4c7b-4712-8619-7de2b23538db_950x306.png 424w, https://substackcdn.com/image/fetch/$s_!r9Su!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0314d5d9-4c7b-4712-8619-7de2b23538db_950x306.png 848w, https://substackcdn.com/image/fetch/$s_!r9Su!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0314d5d9-4c7b-4712-8619-7de2b23538db_950x306.png 1272w, https://substackcdn.com/image/fetch/$s_!r9Su!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0314d5d9-4c7b-4712-8619-7de2b23538db_950x306.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>This amplification effect puts more pressure on a service the further down the chain it is. If you have large dependence chains, it is safer to only retry at one level and fail fast at all the others.</p><h2>Circuit Breakers</h2><p>Timeouts help you find when a downstream service is slow or unreachable, and retries help you get past short-lived failures. But what if the failure doesn't go away quickly?</p>
      <p>
          <a href="https://newsletter.francofernando.com/p/how-to-stop-failures-from-spreading">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[How machine learning and optimization work well together]]></title><description><![CDATA[From making predictions to making choices.]]></description><link>https://newsletter.francofernando.com/p/how-machine-learning-and-optimization</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/how-machine-learning-and-optimization</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Wed, 29 Apr 2026 15:05:34 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/b7dec4b0-9dfb-4b48-ac2f-ac9623dcdb28_1440x686.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends,</p><p>Today&#8217;s special issue features a guest post from Tim Varelmann, founder of <a href="https://bluebird-briefings.kit.com/polymathicengineer">Bluebird Optimization</a>. Tim is a reader of our newsletter. He contacted me to ask if there was an opportunity to collaborate on the newsletter as a guest author for an issue, and I gladly agreed because I think what he has to say could be interesting to you all.</p><p>Tim develops optimization software that helps planners of core business operations make sound, data-driven decisions. He holds a PhD in optimization, with studies at RWTH Aachen, UT Austin, University of Queensland, and MIT, and has combined machine learning and optimization in innovative ways across multiple client projects. </p><p>He is also the author of the world&#8217;s first course on GAMSPy (&#8220;Effortless Modeling in Python with GAMSPy&#8221;). If you want a practical framework for combining ML and optimization from Tim, you can find it <a href="http://bluebird-briefings.kit.com/polymathicengineer">here</a>. Now, over to Tim.</p><div><hr></div><h4><strong><a href="https://coderabbit.link/fernando">Your AI shouldn&#8217;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;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:true,&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="" title="" 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" 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>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 is 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&#8217;s CLI.</a></p><p>Thanks to the CodeRabbit team for collaborating with me on this newsletter issue.</p><div><hr></div><h2>Machine learning and optimization</h2><p>Every day, you make a lot of decisions based on information that doesn&#8217;t actually tell you what to do. Your navigation app knows there will be heavy traffic at 8 am. That is a prediction. What you do about it is a decision that depends on things the app knows nothing about. </p><p>Can you move your first meeting? Is the alternative route through the school zone worth it? Is today the day you finally just work from home? The same forecast, ten different people, ten different choices.</p><p>A big part of our job as engineers is finding an appropriate solution to a problem. </p><p>Netflix works differently. It predicts how much you will enjoy each show in its catalog (typically using Machine Learning). And from that prediction, the decision about what to recommend to you is almost mechanical: sort by the probability that you will like a show, and fill the three recommendation slots with the top results. Here, knowing the forecast basically means knowing the answer.</p><p>Most real-world problems sit somewhere between these two. Predictions matter a lot. But constraints, trade-offs, and competing priorities sit between the forecast and the action. The gap between them is exactly where machine learning and mathematical optimization (or operations research) have to work together.</p><p><em>A forecast tells you what the world looks like. It takes something else to decide what to do in it.</em></p><p>In my experience, the combinations that actually work follow a small number of recurring patterns. Let me show you three of them.</p><h2><strong>Three Combinations of ML &amp; Optimization</strong></h2><p>I keep seeing the same structural roles in different fields, even when applications look very different at first glance:</p><ul><li><p>Making good decisions in the face of uncertain futures</p></li><li><p>Replacing math that is difficult to compute with clearly structured ML models</p></li><li><p>Optimizing systems where no physical laws exist, only observed behavior</p></li></ul><p>There is a fourth pattern where machine learning is used inside optimization solvers themselves, helping them make internal algorithmic choices. This requires deep solver-internal knowledge, which I do not want to presume here. The three patterns above already provide plenty of substance.</p><p>Let&#8217;s start with the most important one.</p><h2><strong>Pattern 1: Making good decisions when the future is uncertain</strong></h2><p>Inventory management makes this pattern very clear. Some products sell at a steady rate. Others barely move for weeks, then suddenly spike. Imagine two spare parts with the same average annual demand. On paper, they look identical. In reality, you should manage them very differently.</p><p><strong>The danger of a single number</strong></p><p>Here is the problem: rich information often dies somewhere between the ML forecast and the optimization model. A probabilistic forecast (one that shows you a whole range of possible futures) gets boiled down to one number, the expected value, before the optimizer ever sees it. This is poison for the combination of ML and optimization.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-EF6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc612d19-f999-4a5a-bb57-d6a72975bcdf_649x304.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-EF6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc612d19-f999-4a5a-bb57-d6a72975bcdf_649x304.jpeg 424w, https://substackcdn.com/image/fetch/$s_!-EF6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc612d19-f999-4a5a-bb57-d6a72975bcdf_649x304.jpeg 848w, https://substackcdn.com/image/fetch/$s_!-EF6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc612d19-f999-4a5a-bb57-d6a72975bcdf_649x304.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!-EF6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc612d19-f999-4a5a-bb57-d6a72975bcdf_649x304.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-EF6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc612d19-f999-4a5a-bb57-d6a72975bcdf_649x304.jpeg" width="649" height="304" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bc612d19-f999-4a5a-bb57-d6a72975bcdf_649x304.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:304,&quot;width&quot;:649,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-EF6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc612d19-f999-4a5a-bb57-d6a72975bcdf_649x304.jpeg 424w, https://substackcdn.com/image/fetch/$s_!-EF6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc612d19-f999-4a5a-bb57-d6a72975bcdf_649x304.jpeg 848w, https://substackcdn.com/image/fetch/$s_!-EF6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc612d19-f999-4a5a-bb57-d6a72975bcdf_649x304.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!-EF6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc612d19-f999-4a5a-bb57-d6a72975bcdf_649x304.jpeg 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>Modern ML models are naturally good at producing rich outputs. Distributions. Confidence intervals. Samples from possible futures. That is exactly the information the optimizer needs. Throwing it away and handing over just the average is like hiring a weather forecaster and only asking them whether it will rain - yes or no.</p><p><strong>What optimization can do with the full picture?</strong></p><p>With the full distribution, the optimizer can distinguish between the two spare parts, even though their averages match.</p><p>For the part with steady demand: order just-in-time, keep safety stock lean.</p><p>For the part with spiky demand: an inventory manager might consciously accept a 10% stockout risk during high-demand phases, if that means avoiding expensive external storage. Maybe that trade-off makes financial sense. Maybe a 15% risk of stockout does not, and the cost for external storage would be preferable. The optimizer finds that line. Without the probabilistic input, it cannot even see the question.</p><p><strong>Where it works - and where it breaks</strong></p><p>This pattern shines when:</p><ul><li><p>Decisions have asymmetric consequences. A missing safety-critical spare part that shuts down a production line is a very different problem from a surplus of optional accessories gathering dust.</p></li><li><p>Different futures require fundamentally different responses. Two parts with the same average demand but different distributions need different inventory strategies.</p></li><li><p>Performance must be robust, not just good on average. If a part is both spiky and safety-critical, you might want guaranteed coverage across two consecutive demand spikes - even if that means holding more stock most of the time.</p></li></ul><p>It breaks when uncertainty gets collapsed too early. If the optimizer only sees one number, it will make one-size-fits-all decisions - and quietly leave money on the table.</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>Pattern 2: Replacing math that is difficult to compute with clearly structured ML models</strong></h2><p>Sometimes the core difficulty is not uncertainty, but complexity. As a researcher, I worked on optimization problems involving air separation units. These are industrial plants that cool air to extremely low temperatures. Oxygen liquefies slightly before nitrogen, so this liquefaction helps separate oxygen from nitrogen.</p><p>The physics involved is well known, but it is also very nonlinear and hard to calculate. Also, these equations are already approximations of the quantum-chemical truth of the world. So, in optimization, the real question wasn&#8217;t whether the equations were &#8220;true,&#8221; but whether they could be used to solve a big optimization problem.</p><p>In that case, my coworkers replaced the detailed thermodynamic relationships with a neural network trained on simulation data.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gIl5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cc50d07-ef30-419a-a152-ede8db92d04a_301x196.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gIl5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cc50d07-ef30-419a-a152-ede8db92d04a_301x196.png 424w, https://substackcdn.com/image/fetch/$s_!gIl5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cc50d07-ef30-419a-a152-ede8db92d04a_301x196.png 848w, https://substackcdn.com/image/fetch/$s_!gIl5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cc50d07-ef30-419a-a152-ede8db92d04a_301x196.png 1272w, https://substackcdn.com/image/fetch/$s_!gIl5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cc50d07-ef30-419a-a152-ede8db92d04a_301x196.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gIl5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cc50d07-ef30-419a-a152-ede8db92d04a_301x196.png" width="301" height="196" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3cc50d07-ef30-419a-a152-ede8db92d04a_301x196.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:196,&quot;width&quot;:301,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gIl5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cc50d07-ef30-419a-a152-ede8db92d04a_301x196.png 424w, https://substackcdn.com/image/fetch/$s_!gIl5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cc50d07-ef30-419a-a152-ede8db92d04a_301x196.png 848w, https://substackcdn.com/image/fetch/$s_!gIl5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cc50d07-ef30-419a-a152-ede8db92d04a_301x196.png 1272w, https://substackcdn.com/image/fetch/$s_!gIl5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3cc50d07-ef30-419a-a152-ede8db92d04a_301x196.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The neural network introduced a small additional approximation error, which was acceptable. In return, the computations became much easier. The optimizer now saw a clean, repeated structure it could work with efficiently.</p><p><em>The win was structure instead of chaos.</em></p><p><strong>Where this pattern works - and where it breaks</strong></p><p>This pattern is particularly powerful when:</p><ul><li><p>Some difficult equations are already approximations, so an additional approximation is not as problematic</p></li><li><p>The true slowdown is computational difficulty, not physical accuracy</p></li></ul><p>It breaks when surrogate models are forced to extrapolate beyond their training data, or when critical structural properties of the original equations are lost in the approximation.</p><h2><strong>Pattern 3: Optimizing systems where no physical laws exist, only observed behavior</strong></h2><p>Some systems simply have no rules in physics that can describe them well. Think of human attention, trust, or preferences. In such cases, behavior must be learned from data.</p><p>In website optimization, for example, ML models can learn how users react to layout changes, rankings, or pricing decisions. This learned behavior is then given to the optimizer, which adjusts the website while respecting multiple other constraints: revenue targets, fairness requirements, budgets, and contracts.</p><p><strong>Where this pattern works - and where it breaks</strong></p><p>This pattern is particularly powerful when:</p><ul><li><p>Human behavior must be observed</p></li><li><p>Sufficient data is available to learn stable behavioral responses</p></li><li><p>Decisions must satisfy non-negotiable constraints (otherwise, an occasional hallucination would be acceptable, but optimization guarantees no hallucinations)</p></li></ul><p>It breaks when learned behavior is treated as static truth, and its drift over time is forgotten.</p><h2><strong>A Match made in Heaven</strong></h2><p>Machine learning is a powerful tool for describing reality from data. Optimization is a strong tool for choosing actions under constraints.</p><p>They work better together when each is used for what it is best at, and when both uncertainty and assumptions are dealt with honestly rather than ignored. Once you find the pattern that your problem fits into, implementation often becomes relatively straightforward.</p><p>If you want to take these ideas further, I have put together a <a href="http://bluebird-briefings.kit.com/polymathicengineer">practical framework for you here</a>. Or just reach out, I am always happy to talk optimization.</p>]]></content:encoded></item><item><title><![CDATA[Linear Regression]]></title><description><![CDATA[How machines learn to draw a line through your data and make predictions.]]></description><link>https://newsletter.francofernando.com/p/linear-regression</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/linear-regression</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Sat, 25 Apr 2026 07:48:19 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/5b8798fd-c8b2-4401-8c30-8149b51fdb97_440x279.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends,</p><p>Welcome to the 170th issue of the Polymathic Engineer newsletter.</p><p>Linear regression is a fundamental algorithm in machine learning. Even though it is simple and intuitive, many engineer treat it as a black box. They call a function from scikit-learn, get a result, and move on.</p><p>But understanding what happens under the hood is critical because linear regression is the basis for more complicated models like neural networks. In this issue, we break down linear regression from the ground up. </p><p>We start with a simple example, build an intuition for how it works, and then explore the math and algorithms that make it all possible. The outline is as follows:</p><ul><li><p>What is Linear Regression?</p></li><li><p>Building an Intuition: Predicting Salaries</p></li><li><p>Features, Weights, and Bias</p></li><li><p>How to Find the Best Line?</p></li><li><p>Measuring How Good a Model Is: Error Functions</p></li><li><p>From One Feature to Many: Multivariate Linear Regression</p></li><li><p>Polynomial Regression</p></li><li><p>Parameters vs Hyperparameters</p></li><li><p>Real-World Applications</p></li><li><p>Putting Everything Into Practice</p></li></ul><p>Regardless of whether you are an experienced engineer who wants to brush up on the basics or a younger engineer exploring machine learning for the first time, this article will give you a solid understanding of how linear regression works and why it matters.</p><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, HTTP server or Git from scratch using your favourite programming language. Noe you can also try to build your own Claude Code (for free, still in beta). </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>What is Linear Regression?</h2><p>Let&#8217;s start with the basics. Say you have a set of data points that roughly look like they are forming a line. The goal of linear regression is to draw the line that passes as close as possible to all of them.</p><p>Think of the data points as houses in a town, and your goal is to build a road that goes through the town. Everyone wants to live near the road, and your job is to make them as happy as possible. The road should go as close as possible to all the homes.</p><p>Of course, this leads to some questions like</p><ul><li><p>What do we mean by &#8220;points that roughly form a line&#8221;?</p></li><li><p>What do we mean by &#8220;a line that passes really close to the points&#8221;?</p></li><li><p>How do we find such a line?</p></li><li><p>Why is this useful in the real world?</p></li></ul><p>We answer all these questions in the following sections.</p><h2>Building an Intuition: Predicting Salaries</h2><p>Let&#8217;s suppose that you want to guess a software engineer&#8217;s salary based on their years of experience. You get some information from a few engineers and get something like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4dT7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1716631b-df7f-49bb-bc5a-dadbb4511e46_1169x455.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4dT7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1716631b-df7f-49bb-bc5a-dadbb4511e46_1169x455.png 424w, https://substackcdn.com/image/fetch/$s_!4dT7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1716631b-df7f-49bb-bc5a-dadbb4511e46_1169x455.png 848w, https://substackcdn.com/image/fetch/$s_!4dT7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1716631b-df7f-49bb-bc5a-dadbb4511e46_1169x455.png 1272w, https://substackcdn.com/image/fetch/$s_!4dT7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1716631b-df7f-49bb-bc5a-dadbb4511e46_1169x455.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4dT7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1716631b-df7f-49bb-bc5a-dadbb4511e46_1169x455.png" width="1169" height="455" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1716631b-df7f-49bb-bc5a-dadbb4511e46_1169x455.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:455,&quot;width&quot;:1169,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:65678,&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/190020077?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1716631b-df7f-49bb-bc5a-dadbb4511e46_1169x455.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_!4dT7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1716631b-df7f-49bb-bc5a-dadbb4511e46_1169x455.png 424w, https://substackcdn.com/image/fetch/$s_!4dT7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1716631b-df7f-49bb-bc5a-dadbb4511e46_1169x455.png 848w, https://substackcdn.com/image/fetch/$s_!4dT7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1716631b-df7f-49bb-bc5a-dadbb4511e46_1169x455.png 1272w, https://substackcdn.com/image/fetch/$s_!4dT7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1716631b-df7f-49bb-bc5a-dadbb4511e46_1169x455.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 plot the data on a coordinate system, with years of experience on the x-axis and salary on the y-axis, you see that the dots make a line that goes up. The more you are experienced, the more money you make (we all know it doesn&#8217;t work exactly like that, but let&#8217;s pretend for the sake of this example).</p><p>Now, let&#8217;s say that a new engineer starts working for your company. She has 4.5 years of experience and you need to make her an offer. There isn&#8217;t a data point for 4.5 years, but you can do it another way: you can find out where 4.5 falls on the line and read the salary. That is linear regression in a nutshell.</p><p>What the model actually learns is a simple equation:</p><p><em>Salary = weight &#215; (years of experience) + bias</em></p><p>The weight shows you how much the salary goes up for each year of experience. The bias is the starting point: starting salary with zero experience. We will get into what these two terms mean in detail in the next section.</p><h2>Features, Weights, and Bias</h2><p>Let&#8217;s have a look at the building blocks of linear regression. There are four things you need to know:</p><ul><li><p><strong>Features: </strong>they are the inputs we use to make a prediction. In our example, years of experience are the feature. But in a more complex model, we could have many features: programming languages known, location, company size, and so on.</p></li><li><p><strong>Label: </strong>they are what we are trying to guess. In our case, the label is the salary.</p></li><li><p><strong>Weights: </strong>they tell the model how much each feature matters. Going back to our equation, the weight might be something like 6,500, since for every extra year of experience, the salary increases by that amount. If the weight was negative, it would mean that more experience is associated with a lower salary, which would be strange. A weight close to zero means the feature barely matters. If you added a feature like <em>the number of monitors </em>on the desk and its weight turned out to be near zero, the model is telling you it doesn&#8217;t help guess the salary.</p></li><li><p><strong>Bias: </strong>it is the starting point. It is the salary the model would guess if the engineer had zero years of experience. In practice, the bias is just there to give the line the flexibility to sit at the right height on the graph.</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_!aBLS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0d57eb4-61f3-4cd1-92ed-3c6905ee9f0b_402x271.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aBLS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0d57eb4-61f3-4cd1-92ed-3c6905ee9f0b_402x271.png 424w, https://substackcdn.com/image/fetch/$s_!aBLS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0d57eb4-61f3-4cd1-92ed-3c6905ee9f0b_402x271.png 848w, https://substackcdn.com/image/fetch/$s_!aBLS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0d57eb4-61f3-4cd1-92ed-3c6905ee9f0b_402x271.png 1272w, https://substackcdn.com/image/fetch/$s_!aBLS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0d57eb4-61f3-4cd1-92ed-3c6905ee9f0b_402x271.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!aBLS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0d57eb4-61f3-4cd1-92ed-3c6905ee9f0b_402x271.png" width="402" height="271" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b0d57eb4-61f3-4cd1-92ed-3c6905ee9f0b_402x271.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:271,&quot;width&quot;:402,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:22499,&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/190020077?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0d57eb4-61f3-4cd1-92ed-3c6905ee9f0b_402x271.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_!aBLS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0d57eb4-61f3-4cd1-92ed-3c6905ee9f0b_402x271.png 424w, https://substackcdn.com/image/fetch/$s_!aBLS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0d57eb4-61f3-4cd1-92ed-3c6905ee9f0b_402x271.png 848w, https://substackcdn.com/image/fetch/$s_!aBLS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0d57eb4-61f3-4cd1-92ed-3c6905ee9f0b_402x271.png 1272w, https://substackcdn.com/image/fetch/$s_!aBLS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0d57eb4-61f3-4cd1-92ed-3c6905ee9f0b_402x271.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 other words, the model finds the best weights and bias values that make the line fit the data. That&#8217;s the whole process of learning. We&#8217;ll talk about how it does that in the next part.</p><h2>How To Find the Best Line?</h2><p>You could draw an endless number of lines through a set of data points. The question is: which one is the best? The answer is the line that is as close as possible to all data points. But we need to be more precise. Here, &#8220;Close to all the points&#8221; means the total distance between the line and the data points is as small as possible. </p><p>In this context, the distance between what the model predicts and what the data point says is called an <strong>error </strong>or <strong>residual</strong>. There are two main ways to find this line.</p>
      <p>
          <a href="https://newsletter.francofernando.com/p/linear-regression">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Finding All Combinations with the The Selection Pattern]]></title><description><![CDATA[How to recursively explore every possible subset using include/exclude decisions.]]></description><link>https://newsletter.francofernando.com/p/finding-all-combinations-with-the</link><guid isPermaLink="false">https://newsletter.francofernando.com/p/finding-all-combinations-with-the</guid><dc:creator><![CDATA[Franco Fernando]]></dc:creator><pubDate>Sat, 18 Apr 2026 12:47:44 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!NUaz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faff94781-9396-411a-ba37-42998198c514_1082x403.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi Friends,</p><p>Welcome to the 169th issue of the Polymathic Engineer newsletter. This week, we continue our recursion series with the third article.</p><p>In the previous two articles, we discussed the <a href="https://newsletter.francofernando.com/p/mastering-recursion-the-foundations">basics of recursion</a> and looked at the first two patterns: <a href="https://newsletter.francofernando.com/p/recursion-in-practice-iteration-and">Iteration and Subproblems</a>. While these patterns are useful, they don&#8217;t go very deep. Now we move on to a stronger pattern: Selection.</p><p>Selection is arguably the most important recursive pattern you can learn. It frequently comes up in coding interviews, and it is the basis for dynamic programming. A lot of problems that seem hard at first become easy once you realize that they are selection problems in disguise.</p><p>The core concept is easy to understand. You look at a set of items and decide whether or not to include each one in your result. By exploring every possible combination of these choices, you generate every possible subset of the original set. From there, you can filter, count, or optimize to get the answer you need.</p><p>This article will explore how this pattern works and how to apply it to real problems.</p><p>The outline is as follows:</p><ul><li><p>The core selection pattern</p></li><li><p>Counting combinations</p></li><li><p>Generating Combinations</p></li><li><p>Combinations of a specific length</p></li><li><p>The 0/1 Knapsack problem</p></li><li><p>When selection applies</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 Core Selection Pattern</h2><p>The selection pattern answers a specific type of question: &#8220;Can this problem be solved by finding all possible combinations?&#8221; If the answer is yes, then you can use the same approach every time.</p><p>The starting point is a set of items. For each item, you have two options: include it in the result or leave it out. You go through the items one by one, and at every step, you branch into two paths. One includes the current item, and the other path excludes it.</p><p>As a concrete example, consider the array [1, 2, 3]. You start at the first item. You can either include 1 or exclude it. If you include it, your partial result is [1]. If you exclude it, your partial result is [].</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NUaz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faff94781-9396-411a-ba37-42998198c514_1082x403.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NUaz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faff94781-9396-411a-ba37-42998198c514_1082x403.png 424w, https://substackcdn.com/image/fetch/$s_!NUaz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faff94781-9396-411a-ba37-42998198c514_1082x403.png 848w, https://substackcdn.com/image/fetch/$s_!NUaz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faff94781-9396-411a-ba37-42998198c514_1082x403.png 1272w, https://substackcdn.com/image/fetch/$s_!NUaz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faff94781-9396-411a-ba37-42998198c514_1082x403.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NUaz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faff94781-9396-411a-ba37-42998198c514_1082x403.png" width="1082" height="403" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/aff94781-9396-411a-ba37-42998198c514_1082x403.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:403,&quot;width&quot;:1082,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:111702,&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/193139411?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faff94781-9396-411a-ba37-42998198c514_1082x403.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_!NUaz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faff94781-9396-411a-ba37-42998198c514_1082x403.png 424w, https://substackcdn.com/image/fetch/$s_!NUaz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faff94781-9396-411a-ba37-42998198c514_1082x403.png 848w, https://substackcdn.com/image/fetch/$s_!NUaz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faff94781-9396-411a-ba37-42998198c514_1082x403.png 1272w, https://substackcdn.com/image/fetch/$s_!NUaz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faff94781-9396-411a-ba37-42998198c514_1082x403.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>Now you move to the second item. For each of the two partial results, you again have two choices. Include or exclude 2. This gives you four partial results: [1, 2], [1], [2], and []. You continue this process for the third item. Each of the four results branches into two, and you end up with eight total combinations: [1, 2, 3], [1, 2], [1, 3], [1], [2, 3], [2], [3], and []. This is the complete set of subsets. </p><p>There are always 2^n subsets for an array of n elements, including the empty set. The selection pattern generates all of them by examining every possible path through the decision tree.</p><p>The word &#8220;selection&#8221; comes from the choice you make at each step. You are selecting which items to include in your result. People also call this the "combinations" pattern, but I prefer "selection" because it captures what you do: you either choose the item, or you don't.</p><h2>Counting Combinations</h2><p>Before looking at how to generate all the combinations, it helps to start with a simpler task: counting how many combinations exist. This is a good approach since it lets you focus on the recursive structure without worrying about building up results.</p><p>The logic is straightforward. You return 1 if you have reached the end of the array and discovered one valid combination. Otherwise, you make two recursive calls, one which includes the current item and one that excludes it. You add the results together.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;2bd45dfb-ae69-4d6e-8f7d-2f6223b71780&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def count_combinations(arr, i=0):
    if i == len(arr):
        return 1

    include = count_combinations(arr, i + 1)
    exclude = count_combinations(arr, i + 1)

    return include + exclude</code></pre></div><p>Let&#8217;s trace what happens when we run this code with the array [1, 2, 3]. The initial call branches into two: one where we include 1 and one where we don't. Then we move to item 2, and each of those branches splits into two other branches. The same happens for item 3. When we reach the end of the array, we return 1. All those 1s add up as the calls return, and we get 8 at the top.</p><p>You might notice that the include and exclude calls are identical. That is because the number of remaining combinations stays the same regardless of whether you include an item. There are always the same number of paths forward. This will not hold when we add constraints.</p><p>The time complexity is O(2^n) because we make two recursive calls at each level, and the depth is n. The space complexity is O(n) because of the call stack. These numbers match what we expect: there are 2^n combinations, and we visit each one.</p><h2>Generating Combinations</h2><p>Counting is straightforward, but for most problems, you need to actually generate the combinations. This is where things get more interesting. There are two main ways to do this: either build up the results as you return them, or update a passed variable as you go.</p>
      <p>
          <a href="https://newsletter.francofernando.com/p/finding-all-combinations-with-the">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><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 codeba&#8230;</p>
      <p>
          <a href="https://newsletter.francofernando.com/p/software-design-principles-that-matter">
              Read more
          </a>
      </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></channel></rss>