<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: João Godinho</title>
    <description>The latest articles on DEV Community by João Godinho (@godinhojoao).</description>
    <link>https://dev.to/godinhojoao</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1138510%2Fbec564f8-ceaa-4f60-ba56-931ec1b923dc.jpeg</url>
      <title>DEV Community: João Godinho</title>
      <link>https://dev.to/godinhojoao</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/godinhojoao"/>
    <language>en</language>
    <item>
      <title>Tail Latency</title>
      <dc:creator>João Godinho</dc:creator>
      <pubDate>Mon, 24 Aug 2026 02:20:31 +0000</pubDate>
      <link>https://dev.to/godinhojoao/tail-latency-3dhm</link>
      <guid>https://dev.to/godinhojoao/tail-latency-3dhm</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This article covers Tail at Scale by Jeffrey Dean and Luiz André Barroso, both contributed to building what we call today modern data centers and framed an important topic and its effects in systems at large scale called Tail Latency (coining the term "tail-tolerant").&lt;/li&gt;
&lt;li&gt;Tail Latency is a topic that Google Search and other services take advantage of to avoid latency issues and reach that performance that we observe in our daily life.&lt;/li&gt;
&lt;li&gt;We will discuss more than just &lt;strong&gt;"What is Tail Latency?"&lt;/strong&gt;, but also:

&lt;ul&gt;
&lt;li&gt;What are p50, p99, p99.9, and other percentiles?&lt;/li&gt;
&lt;li&gt;Why both average and median lie?&lt;/li&gt;
&lt;li&gt;What causes tail latency?&lt;/li&gt;
&lt;li&gt;Parallelizing requests across multiple distributed systems using Fan-Out and Fan-In techniques.&lt;/li&gt;
&lt;li&gt;The effects of variability at small and large scale.&lt;/li&gt;
&lt;li&gt;Can we kill variability or do we need to understand how to engineer on top of that fact?&lt;/li&gt;
&lt;li&gt;What is the naive "latency fix" and why it spends unnecessary money, computing power and energy resources?&lt;/li&gt;
&lt;li&gt;How to apply that knowledge in the systems you are building? How to collect metrics, analyze them and take action? (prometheus, grafana, and k6)&lt;/li&gt;
&lt;li&gt;The risks of gathering incorrect data in load tests: Coordinated omission.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is p50, p99, p99.9, and other percentiles?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Imagine you have a line cut into 100 equal pieces from left to right. The 50th piece represents the p50, also called the median.&lt;/li&gt;
&lt;li&gt;The 99th piece is almost the last one closer to the right side.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Now imagine that this line represents requests sorted by their latency, where the leftmost one has the smallest latency and the further right we go, the higher the latency is.&lt;/li&gt;
&lt;li&gt;When someone says "The p99 is 1s", it means: "1 of 100 users is facing a response time of 1 second or more."&lt;/li&gt;
&lt;li&gt;When someone says "p50 is 100ms", it means: "50 of 100 users are facing a response time under 100ms"&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;To understand p99.9 we need to cut this line not in 100 pieces, but in 1000 smaller parts. It is the rightmost part, the 999th.&lt;/li&gt;
&lt;li&gt;"p99.9 is 10s" = "1 of 1000 users is facing a response time of 10 seconds or more."&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why both Average and Median (p50) lie?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Average&lt;/strong&gt; gets pulled by higher values, but at scale one slow request barely changes it, since that single value gets diluted across all the fast ones. It seems good for showing us problems, but it actually hides the tail.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Median&lt;/strong&gt; is completely blind to the tail, we could make the 49% slowest requests take hours and the median would not move.

&lt;ul&gt;
&lt;li&gt;p50(1,2,3,500) = (2+3)/2 = 2.5&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Tail Latency and What Causes it?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What causes Tail Latency?&lt;/strong&gt; Variability, response times are not constant, they spread out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What is Tail Latency?&lt;/strong&gt; The slow end of that spread of response times far above the median. It is a region of the distribution, not a single latency value.

&lt;ul&gt;
&lt;li&gt;p99 and p99.9 are the metrics we use to measure the tail, and identify how bad the tail is.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  And Why Latency Variability Exists?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multiple factors, such as:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cache miss&lt;/strong&gt;: forcing a slower path through the disk or doing network calls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared resources&lt;/strong&gt;: CPU cores, processor caches, memory bandwidth, network bandwidth...&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queueing&lt;/strong&gt;: Requests do not arrive evenly, they come in bursts. If more arrive than the server can process, requests pile up in the queue and wait, which increases the total request time a lot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background maintenance activities&lt;/strong&gt;: such as cronjobs, log compaction, data reconstruction in distributed file systems, etc...&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Garbage Collector&lt;/strong&gt;: The runtime of garbage-collected languages (e.g. Java) periodically pauses normal work to reclaim unused memory. Unlucky requests will get frozen for the duration of it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Even power limits&lt;/strong&gt; used in hardware to control temperature of modern CPUs and other components.&lt;/li&gt;
&lt;li&gt;These and much more...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Fan-Out and Fan-In?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A technique used to turn a unit of work into multiple parallel units of work (1:M).&lt;/li&gt;
&lt;li&gt;It can happen in the same server using different threads.&lt;/li&gt;
&lt;li&gt;But in distributed systems it can also mean parallelizing requests across multiple different services and then merging their results into a single response. In this way you are not only limited to a single machine's cores to parallelize tasks, but to multiple nodes.

&lt;ul&gt;
&lt;li&gt;The distributed systems concepts are very similar to the &lt;strong&gt;fork/join&lt;/strong&gt; concepts when working with threads in a single machine.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fan-Out&lt;/strong&gt; = split a larger task into smaller sub tasks; root server distributes sub tasks to other services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fan-In&lt;/strong&gt; = used if you need to wait for all sub tasks to complete before moving to the next processing step; root receives the responses and merges them into a single response.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NO, Fan-In isn't required after Fan-Out!&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  How Scale Amplifies Variability Effects
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If you don't understand the probabilities, I suggest you watch &lt;a href="https://www.khanacademy.org/math/statistics-probability/probability-library/multiplication-rule-dependent/v/independent-events-1" rel="noopener noreferrer"&gt;Independent &amp;amp; dependent probability - Khan Academy&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;As you can see the effect of variability at scale can be a killer for your services. Imagine if Google hadn't cared about that, would they be that huge, or would customers use other, faster services?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Can we kill variability?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;No, eliminating all variability is infeasible, so you build &lt;strong&gt;tail-tolerant systems&lt;/strong&gt; instead. &lt;strong&gt;So how to do that?&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1. Fix the node first
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Before any sophisticated technique we start simple.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run the server at realtime priority&lt;/strong&gt; so background daemons cannot preempt it. (OS scheduling)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dedicate a core to interrupts&lt;/strong&gt; so network packet interrupts do not keep stealing the application's cores.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Within-request techniques
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;These act inside a single request, in tens of milliseconds, and reuse replicas you already keep for availability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Hedged requests&lt;/strong&gt;: if the &lt;strong&gt;request&lt;/strong&gt; to the first replica (that seemed to be the most appropriate) &lt;strong&gt;lasts for more than p95&lt;/strong&gt;, we &lt;strong&gt;send a copy to another replica&lt;/strong&gt; and take whichever replies first cancelling the rest.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's especially good because if the first server hit a bad moment (GC pause, etc) the second rescues the application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Waiting too short (hedge at p50)&lt;/strong&gt; = 50% of requests duplicated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Waiting too long (hedge at p99.9)&lt;/strong&gt; = Almost no duplicates, but the response is very slow before help arrives.&lt;/li&gt;
&lt;li&gt;Because of that, the authors of the article "The Tail at Scale" suggest &lt;strong&gt;hedging at p95&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tied requests&lt;/strong&gt;: Send the request to two servers at once, each tagged with the other's identity. Whichever begins execution first sends a cancel to its twin, so our app rarely pays for double work.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Cross-request techniques
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;These handle coarser problems (load imbalance, or a particularly slow machine) over seconds to minutes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Micro-partitions&lt;/strong&gt;: Create many more partitions (data chunks) than machines, so you can move load between machines in small steps instead of moving a whole machine's worth at once. With 20 partitions per machine you shed load in 5% increments and rebalance much faster, since each move copies only a small chunk.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Selective replication&lt;/strong&gt;: Detect (or predict) the items likely to get hot and make extra copies of just those, so the load balancer can spread that heat without moving whole partitions around.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Latency-induced probation&lt;/strong&gt;: Watch each machine's latency and temporarily remove one that has turned slow, while still sending test requests to it in the background so you know when it recovers and can bring it back.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It sounds counterintuitive, &lt;strong&gt;why would removing one slow machine help reduce latency and build a tail-tolerant system?&lt;/strong&gt; Because we fan out and wait for all of them, so the slowest one sets the latency of the whole request. Removing it leaves only the fast ones, and we keep probing the slow one to bring it back once it recovers.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Information-retrieval techniques
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;For search-like systems where a good answer now beats a perfect answer later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Good-enough responses&lt;/strong&gt;: Once enough leaf servers have replied, return slightly incomplete results instead of waiting for slower ones.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Canary requests&lt;/strong&gt;: Send the request to one or two leaves first, and only fan out to the rest if the canary comes back cleanly in a reasonable period of time, protecting against a bad request that could stall thousands of servers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Canary" comes from the idea that miners used a bird to see if the mine had poison gas so if the bird died they could leave safely.&lt;/li&gt;
&lt;li&gt;Same as we do with canary deployment, slowly routing traffic to the server and if it dies we roll back to the stable one.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The naive fix and unnecessary waste of money
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;High utilization makes tail latency much worse.

&lt;ul&gt;
&lt;li&gt;Imagine a supermarket cashier idle, then 3 customers arrive at once and they are served right away, no line forms.&lt;/li&gt;
&lt;li&gt;Now what if the cashier was busy most of the time? Then the same 3 customers at once will wait a long time.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The obvious move is to keep servers 30-40% busy, so there is always slack to handle incoming requests. It works and is pretty expensive as you can imagine.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Naive approach: Paying for 100 servers to do the work of 40 just to reduce tail latency having 60 servers for latency insurance&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The right approach mentioned in the previous section: &lt;strong&gt;techniques to build tail-tolerant systems&lt;/strong&gt;.

&lt;ul&gt;
&lt;li&gt;Hedged requests and tied requests lean on already existent replicas for fault tolerance, not new servers to avoid tail latency.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tail-tolerant techniques let us keep the tail low while running servers hot, buying fewer machines.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Measuring the Tail in Real Systems
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;We could gather latency data using monitoring systems such as &lt;a href="https://prometheus.io/" rel="noopener noreferrer"&gt;Prometheus&lt;/a&gt; and an observability system such as &lt;a href="https://grafana.com/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt; to build charts and dashboards to analyze these data.&lt;/li&gt;
&lt;li&gt;We could also implement a &lt;strong&gt;load test&lt;/strong&gt; on our service and analyze the results in charts and dashboards using &lt;a href="https://k6.io/" rel="noopener noreferrer"&gt;k6&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Now take a look at these K6 load test results: &lt;a href="https://github.com/godinhojoao/k6-load-stress-tests-api" rel="noopener noreferrer"&gt;Github - K6 load test POC&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Below I'll show only the latency and some percentiles, but k6 provides us much more information.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftodb6iyyg6xy3c23lyme.png" alt="k6-cut-summary" width="800" height="127"&gt;
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3621hsn53rp9wvkg7g9l.png" alt="k6-latency-chart" width="800" height="258"&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The overall p99 is 220ms which is good. The warmup pushed p99 above 1s which looks bad, but since it was just the warmup that spike is expected and not a real concern.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;One problem of load test tools if misconfigured is Coordinated omission&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Coordinated omission&lt;/strong&gt; happens when your load tester keeps waiting on a slow request instead of sending the next ones on schedule, so it silently skips the requests that would have been slow too, making your measured p99 look far better than reality.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Named by Gil Tene on &lt;a href="https://www.infoq.com/presentations/latency-pitfalls/" rel="noopener noreferrer"&gt;How NOT to Measure Latency&lt;/a&gt; (coordinated omission starts around 33:50).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/epdf/10.1145/2408776.2408794" rel="noopener noreferrer"&gt;The Tail at Scale by Jeffrey Dean and Luiz André Barroso (CACM)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/epdf/10.1145/2670979.2670988" rel="noopener noreferrer"&gt;Tales of the Tail: Hardware, OS, and Application-level Sources of Tail Latency (SOCC 2014)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.openfaas.com/blog/fan-out-and-back-in-using-functions/" rel="noopener noreferrer"&gt;Exploring the Fan out and Fan in pattern with OpenFaaS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://grafana.com/docs/tempo/latest/metrics-from-traces/service_graphs/metrics-queries/" rel="noopener noreferrer"&gt;Analyze service graph data - Grafana&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.infoq.com/presentations/latency-pitfalls/" rel="noopener noreferrer"&gt;How NOT to Measure Latency by Gil Tene&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>distributedsystems</category>
      <category>computerscience</category>
      <category>systemdesign</category>
      <category>sre</category>
    </item>
    <item>
      <title>Service Levels: Understanding SLIs, SLOs, and SLAs</title>
      <dc:creator>João Godinho</dc:creator>
      <pubDate>Fri, 21 Aug 2026 02:02:45 +0000</pubDate>
      <link>https://dev.to/godinhojoao/service-levels-understanding-slis-slos-and-slas-2266</link>
      <guid>https://dev.to/godinhojoao/service-levels-understanding-slis-slos-and-slas-2266</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This article covers Google Site Reliability Engineering (SRE) book material and Google Cloud material about service levels: SLIs, SLOs, and SLAs.&lt;/li&gt;
&lt;li&gt;What are service levels? Their indicators, objectives, agreements and consequences.&lt;/li&gt;
&lt;li&gt;Which metrics to gather, how to gather, and why?&lt;/li&gt;
&lt;li&gt;Which objectives to set, and why?&lt;/li&gt;
&lt;li&gt;When can we turn an SLO into an SLA?&lt;/li&gt;
&lt;li&gt;Why is it so important, if you really want to reduce churn and customer's disappointment?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What are Service Levels
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The measured quality of a service, expressed as a quantified target that matches user needs. It can be: availability, latency, throughput, error rate, data freshness, and more.&lt;/li&gt;
&lt;li&gt;Even correctness can be measured: the proportion of requests that returned the right answers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why do we need service levels?&lt;/strong&gt; Measurement is the key, saying "reliable system" isn't enough, we need to measure and verify it constantly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Service Level Indicator (SLI)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;SLI is the quantitative indicator, the metric, and it has observed values for those metrics.&lt;/li&gt;
&lt;li&gt;SLIs examples: &lt;strong&gt;p99 = 200ms&lt;/strong&gt;; &lt;strong&gt;correctness (right responses) = 99.7%&lt;/strong&gt;; &lt;strong&gt;availability = 99.9% (three 9s)&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;Another good example is: &lt;strong&gt;data freshness&lt;/strong&gt; = data that is &lt;strong&gt;less than 10 seconds old&lt;/strong&gt; (current time - data timestamp) =&amp;gt; &lt;strong&gt;99 of 100 = 99%&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Service Level Objective (SLO)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What we want to achieve in our system: &lt;strong&gt;The target we want the SLI to achieve&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;It should be reliable enough to meet user needs, but not so tight that it becomes expensive and hard to maintain.&lt;/li&gt;
&lt;li&gt;Generally people think the harder or bigger the SLO, the better, but this is not the idea, we will discuss it soon after covering basics.&lt;/li&gt;
&lt;li&gt;SLO example: &lt;strong&gt;SLI: Latency - SLO: p99 &amp;lt; 200ms&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Service Level Agreement (SLA)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An agreement with the customers about metrics the system will deliver, and the consequences if the agreed service level isn't met.&lt;/li&gt;
&lt;li&gt;SLA example: &lt;strong&gt;SLA: 99.9% availability - if it falls below this, the customer receives 10% credit.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Target without contractual consequence = SLO.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Visual Flow of Service Levels
&lt;/h2&gt;

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

&lt;h2&gt;
  
  
  Collecting Indicators
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;For that use a monitoring system such as &lt;a href="https://prometheus.io/" rel="noopener noreferrer"&gt;Prometheus&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Don't rely only on backend metrics, also track frontend. Since user experience and user satisfaction are our objective we should also track metrics and cover problems that happen only on frontend. For example, slow page TTFB (time to first byte), and other issues.&lt;/li&gt;
&lt;li&gt;Common metrics and interpretations:

&lt;ul&gt;
&lt;li&gt;p50 = median latency (50% of requests are faster than this)&lt;/li&gt;
&lt;li&gt;p99 = tail latency (99% of requests are faster than this)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why is p99 called "tail" latency?&lt;/strong&gt; Because p99 measures the latency near the end of the distribution, where the slowest 1% of requests are.&lt;/li&gt;
&lt;li&gt;more variance = more tail latency effect&lt;/li&gt;
&lt;li&gt;Avoid variance, Google researched that users prefer slightly slower responses than high variance response times and unpredictability.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Standardizing Indicators
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create a SLI template with default settings.&lt;/strong&gt; It is especially useful when starting with service levels to help understanding.&lt;/li&gt;
&lt;li&gt;Example:

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Perform metrics aggregation every 1 min&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Aggregation region = all tasks of entire cluster&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Measure every 10s&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data-access latency = time to last byte&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Which routes to monitor and measure? Features critical flow; the most important ones for users&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;These I've told you are examples, don't follow them blindly.&lt;/li&gt;
&lt;li&gt;Google SRE book mentions it as examples. I believe it's a good start.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Which are Possible SLOs and How to Choose Them?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;As we've discussed earlier the SLOs can be multiple, and they are based on the SLIs.&lt;/li&gt;
&lt;li&gt;Common indicators are: availability, latency, throughput, correctness, data freshness...&lt;/li&gt;
&lt;li&gt;But having multiple indicators is not the point, the key is understanding your users and their needs. And only then setting SLOs to match their expectations and increase customer satisfaction.&lt;/li&gt;
&lt;li&gt;There are a lot of things to consider on user behavior and research you should do to achieve this, since this is not the focus of this post I'll limit myself in this topic here.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  SLOS are Objectives, Not Ornaments
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Don't create SLOs only because they are on top of indicators easy to measure and objectives easy to achieve.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It's better to have only a couple of SLOs that really match user expectations than easy and useless ones.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;If you see that it is a good strategy to "categorize" users and identify needs for these categories, then you can see which ones to focus on. This is especially good when user needs vary a lot.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Tips from Google SRE book:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Insisting that SLOs be met 100% of the time kills innovation and software delivery, and is pretty expensive.&lt;/li&gt;
&lt;li&gt;Keep it simple, don't build complex SLI aggregates.&lt;/li&gt;
&lt;li&gt;Have as few SLOs as possible (just enough to meet user needs - understand your customers.)&lt;/li&gt;
&lt;li&gt;Don't expect user needs will be easy to match, find the right balance.&lt;/li&gt;
&lt;li&gt;Perfection can wait, find a good margin not too tight but good enough for users.&lt;/li&gt;
&lt;li&gt;Keep a safety margin internally tighter than the public one.&lt;/li&gt;
&lt;li&gt;SLA requires time, and analysis of the difficulty to meet it across time. If you can't ensure it, don't create one.&lt;/li&gt;
&lt;li&gt;Research, run experiments and tests to understand your users' needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/blog/products/devops-sre/sre-fundamentals-sli-vs-slo-vs-sla" rel="noopener noreferrer"&gt;SRE fundamentals: SLI vs SLO vs SLA (Google Cloud Blog)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sre.google/sre-book/service-level-objectives/" rel="noopener noreferrer"&gt;Service Level Objectives (Google SRE Book, Chapter 4)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sre.google/resources/practices-and-processes/art-of-slos/" rel="noopener noreferrer"&gt;The Art of SLOs (Google Site Reliability Engineering)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://research.google/blog/speed-matters/" rel="noopener noreferrer"&gt;Speed Matters (Google Research Blog)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://googleblog.blogspot.com/2008/08/search-experiments-large-and-small.html" rel="noopener noreferrer"&gt;Search experiments, large and small (Google Official Blog)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>systemdesign</category>
      <category>sre</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Requirements &amp; Back-of-the-Envelope Calculation</title>
      <dc:creator>João Godinho</dc:creator>
      <pubDate>Wed, 19 Aug 2026 23:01:43 +0000</pubDate>
      <link>https://dev.to/godinhojoao/requirements-back-of-the-envelope-calculation-5gnl</link>
      <guid>https://dev.to/godinhojoao/requirements-back-of-the-envelope-calculation-5gnl</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;This article covers how to size a system before building it: first gathering functional and non-functional requirements, then running back-of-the-envelope calculations (QPS, storage, bandwidth, cache, and server count) through a worked example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why estimation matters
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Using estimations we can go from "build a url shortener" to concrete numbers:

&lt;ul&gt;
&lt;li&gt;Queries per second (QPS)&lt;/li&gt;
&lt;li&gt;storage/year&lt;/li&gt;
&lt;li&gt;bandwidth&lt;/li&gt;
&lt;li&gt;server count&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;And with that you can spot bottlenecks before they turn into a problem.&lt;/li&gt;
&lt;li&gt;It provides us the ability to think about feasibility and scale, and drives capacity planning: how many servers, how much cache, how many DB shards, how much network bandwidth...&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Before talking about estimates
&lt;/h2&gt;

&lt;p&gt;Before estimating anything, pin down the requirements: what the system must do (functional) and how it must behave (non-functional).&lt;/p&gt;

&lt;h2&gt;
  
  
  Functional Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Describes what the system does, features from the user's perspective. "Ask yourself what can a user do in my app?"

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Twitter&lt;/strong&gt;: post a tweet, follow another user, timeline, like/comment a tweet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Youtube&lt;/strong&gt;: upload a video, play back a video, search for videos, like/share a video, recommendation feed.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;As you can see these behaviors map directly to APIs and core use cases.&lt;/li&gt;
&lt;li&gt;Trying to do everything, and losing time-to-market on your core features, is terrible.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to gather Functional Requirements?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Think about the system, talk with stakeholders, and teammates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;List primary usecases &amp;amp;&amp;amp; Identify the actors&lt;/strong&gt;: "In this uber-like app, clients request a driver and drivers can accept or reject clients. Will clients be able to rate drivers? And vice-versa?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Think about edge scenarios to consider&lt;/strong&gt;: "What happens if no driver accepts the request? Can a client cancel after a driver already accepted? What if the driver cancels mid-ride, or the app loses connection during the trip?"&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Non-Functional Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Describes how the system behaves, the quality attributes and constraints: performance, reliability, and user experience.&lt;/li&gt;
&lt;li&gt;The architectural trade-offs live here (e.g. read vs write optimized, strong vs eventual consistency) and also estimation targets (QPS, storage, latency, budgets).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always attach numbers&lt;/strong&gt;, don't be vague and say "the system must be fast and highly available", pin concrete targets to each attribute "p99 latency &amp;lt; 200ms" and "99.9% availability".&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Some Important Categories of Non-Functional Requirements:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: ability to handle growth in users/traffic/data.

&lt;ul&gt;
&lt;li&gt;1M daily active users; 500M tweets/day; 10k requests/sec&lt;/li&gt;
&lt;li&gt;"Do we need geographical distribution or just a local region?"&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance (latency and throughput)&lt;/strong&gt;: speed and responsiveness.

&lt;ul&gt;
&lt;li&gt;p99 response &amp;lt; 200ms; sub-second search queries&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Availability&lt;/strong&gt;: Fraction of time the system is up.

&lt;ul&gt;
&lt;li&gt;99.9% (three nines) vs 99.99% (four nines) uptime.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliability&lt;/strong&gt;: Correct operation, fault tolerance, no single point of failure (SPOF).

&lt;ul&gt;
&lt;li&gt;Survive one AZ/node failure and still return the right responses.&lt;/li&gt;
&lt;li&gt;Redundant app servers, db replica, automatic failover, etc.&lt;/li&gt;
&lt;li&gt;Automatic failover = the system detects when a component failed and switches to a healthy backup on its own.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Durability&lt;/strong&gt;: Data survives failures, no data loss.

&lt;ul&gt;
&lt;li&gt;No committed write ever lost; e.g. AWS S3 is designed for 11 9s of object durability (99.999999999%)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: how up-to-date reads are across replicas.

&lt;ul&gt;
&lt;li&gt;Strong (banking balances) vs eventual within a few seconds (social media posts).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security and Privacy&lt;/strong&gt;: Protection of data and access.

&lt;ul&gt;
&lt;li&gt;Auth, encryption in transit + at rest, rate limiting, GDPR compliance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;encryption at rest&lt;/strong&gt; = data is encrypted while stored. (disk, database, backups)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;encryption in transit&lt;/strong&gt; = data is encrypted while moving between two points. (HTTPS)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainability/Monitoring&lt;/strong&gt;: Ease of change; Logging and monitoring.

&lt;ul&gt;
&lt;li&gt;Can also be non-functional requirements.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to gather Non-Functional Requirements?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Generally developers only think about functional requirements and it only turns into a problem when software is shipped to production and users start to face slow responses and loadings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple: question yourself or stakeholders about project's non-functional requirements.&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;How many daily active users?&lt;/li&gt;
&lt;li&gt;Requests per second?&lt;/li&gt;
&lt;li&gt;Most requests under 200ms (p99 &amp;lt;200ms)?&lt;/li&gt;
&lt;li&gt;Can this data be eventually consistent within a few seconds?&lt;/li&gt;
&lt;li&gt;Is there an uptime target or no SPOF requirement (more than one server instance, DB, etc)?&lt;/li&gt;
&lt;li&gt;Do we have any reliability constraint, if one server goes down should we have two or more? Geographically distributed or same region?&lt;/li&gt;
&lt;li&gt;Any GDPR concerns, or special security/privacy requirements?&lt;/li&gt;
&lt;li&gt;Can users see stale data for a few seconds as a trade-off to reach the required faster responses?&lt;/li&gt;
&lt;li&gt;Is there a budget or cost ceiling constraining the design?&lt;/li&gt;
&lt;li&gt;Is it read-heavy or write-heavy, and how big are peaks vs average?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Obs&lt;/strong&gt;: while talking with non-technical people we could ask the same questions in a different style to gather non-functional requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Back-of-the-Envelope Estimation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Quick, rough calculations to size a system in minutes. It's not meant to be exact, it's just a good approximation to check feasibility and find bottlenecks.&lt;/li&gt;
&lt;li&gt;Generally it uses rules of thumb, general guidelines or principles based on experience and observation, not 100% precise but helpful for back-of-the-envelope calculations.&lt;/li&gt;
&lt;li&gt;First of all, you must know basic measures used for these quick calculations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Data Volume Units (Powers of Two)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;2^10 ~= 1 Thousand -&amp;gt; 1 KB (Kilobyte)&lt;/li&gt;
&lt;li&gt;2^20 ~= 1 Million -&amp;gt; 1 MB (Megabyte)&lt;/li&gt;
&lt;li&gt;2^30 ~= 1 Billion -&amp;gt; 1 GB (Gigabyte)&lt;/li&gt;
&lt;li&gt;2^40 ~= 1 Trillion -&amp;gt; 1 TB (Terabyte)&lt;/li&gt;
&lt;li&gt;2^50 ~= 1 Quadrillion -&amp;gt; 1 PB (Petabyte)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Latency numbers every programmer should know (Peter Norvig and Jeff Dean)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;These numbers are dated, but &lt;strong&gt;the point isn't accuracy, it's the relative ratios / orders of magnitude between tiers (memory vs SSD vs disk vs network)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Memory is fast, disk is slow, network across regions is slowest (avoid disk and cross-region hops on the hot path).&lt;/li&gt;
&lt;li&gt;If you need to send data across the network, compress it.&lt;/li&gt;
&lt;li&gt;Caching in memory can turn a 30ms disk read into a ~100ns lookup.&lt;/li&gt;
&lt;li&gt;A cross-continent round trip (~150ms) dominates everything else (put data near users).&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Table from: &lt;a href="https://bytebytego.com/courses/system-design-interview/back-of-the-envelope-estimation" rel="noopener noreferrer"&gt;ByteByteGo&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ms = millisecond (10^-3 seconds)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;µs = microsecond (10^-6 seconds)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ns = nanosecond (10^-9 seconds)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Estimation example
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Estimating write QPS&lt;/strong&gt;: start from &lt;strong&gt;daily active users (DAU)&lt;/strong&gt; * &lt;strong&gt;actions per user per day&lt;/strong&gt;, then convert to per-second (divide by the seconds in one day 86400)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;100k daily active users (DAU)&lt;/li&gt;
&lt;li&gt;each user posts 3 photos per day (a photo post is a write)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;writes per day = 100,000 * 3 = 300,000 photos/day
write QPS = 300,000 / 86,400 = (3 * 10^5) / (8.64 * 10^4) = 30 / 8.64 ~= 3
(it doesn't need to be exact, it is an approximation)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;write QPS = 3&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Estimate peak QPS&lt;/strong&gt;: multiply average QPS by a peak factor (2x-10x) to plan for spikes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PEAK write QPS = 2 * 3 = 6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Estimate read vs write QPS&lt;/strong&gt;: apply the read/write ratio (this system is read-heavy, &lt;code&gt;100:1&lt;/code&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;read QPS = 100 * 3 = 300
PEAK read QPS = 2 * 300 = 600
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Estimate storage&lt;/strong&gt; = objects per day * size per object * retention period * overhead factor&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;overhead factor = metadata, indexes, replication, etc... (e.g. double overhead = 2)&lt;/li&gt;
&lt;li&gt;assumptions: 3mb avg photo size, 4 years retention, overhead factor = 2, writes = 300,000 photos/day (from step 1)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;storage = 300,000 * 3mb * 365 * 4 * 2 ~= 2.6PB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Estimate bandwidth&lt;/strong&gt; = QPS * payload size (split into ingress = writes, egress = reads)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ingress (writes) = write QPS * size = 3 * 3mb = 9 mb/s
egress (reads) = read QPS * size = 300 * 3mb = 900 mb/s
(peak 600 * 3mb = 1.8 gb/s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;reads dominate (read-heavy), use peak QPS for the peak value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;6. Estimate memory/cache&lt;/strong&gt;: use a rule of thumb -&amp;gt; Pareto principle (cache the hot 20% that serves 80% of reads).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cache = 20% of daily data = (300,000 * 3mb) * 0.2 = 900 GB * 0.2 = 180 GB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;7. Estimate server count&lt;/strong&gt; = required QPS / QPS a single server can handle (+redundancy for safety)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the dominant load is reads, so size on &lt;code&gt;PEAK read QPS = 600&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;assume 1 server handles ~&lt;code&gt;1,000 QPS&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;600 / 1,000 &amp;lt; 1 -&amp;gt; 1 server already handles the load
Server count = 1 + 1 = 2
(the +1 is for redundancy/availability, so there is no
single point of failure if one goes down)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What the numbers told us&lt;/strong&gt;: it's a read-heavy service (&lt;code&gt;100:1&lt;/code&gt;), so favor caching + read replicas and a CDN for media. Storage is large and media-dominated (&lt;code&gt;~2.6PB&lt;/code&gt; over 4 years), so use object storage with tiering, not a single DB. Egress dominates bandwidth (&lt;code&gt;~900 mb/s&lt;/code&gt;, peak &lt;code&gt;~1.8 gb/s&lt;/code&gt;), which is why media should go through a CDN. The request load itself is small (peak &lt;code&gt;600&lt;/code&gt; read QPS), so 1 app server already handles it (plus 1 more for redundancy, &lt;code&gt;2&lt;/code&gt; total) alongside a cache tier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Gathering requirements, knowing about functional and non-functional requirements, and doing back-of-the-envelope calculations are important skills that every system architect must have.&lt;/li&gt;
&lt;li&gt;Even if you didn't know these before this reading, if you already did that instinctively you were on the right path.&lt;/li&gt;
&lt;li&gt;The biggest mistake of some developers is trying to develop a system when they don't know its functional and non-functional requirements. It will certainly be a mess and full of issues.

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Developing the wrong system well is worse than developing the right system poorly.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Obviously what we've discussed is just the start. The most important part is experience: doing and repeating.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/donnemartin/system-design-primer" rel="noopener noreferrer"&gt;System Design Primer&lt;/a&gt; (Donne Martin, GitHub)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bytebytego.com/courses/system-design-interview/back-of-the-envelope-estimation" rel="noopener noreferrer"&gt;Back-of-the-Envelope Estimation&lt;/a&gt; (ByteByteGo)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.norvig.com/21-days.html#answers" rel="noopener noreferrer"&gt;Teach Yourself Programming in Ten Years&lt;/a&gt; (Peter Norvig, the latency numbers table)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.designgurus.io/blog/how-to-gather-requirements-in-system-design-interviews" rel="noopener noreferrer"&gt;How to Gather Requirements in System Design Interviews&lt;/a&gt; (DesignGurus)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>systemdesign</category>
      <category>distributedsystems</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Deployment strategies</title>
      <dc:creator>João Godinho</dc:creator>
      <pubDate>Wed, 12 Aug 2026 01:34:50 +0000</pubDate>
      <link>https://dev.to/godinhojoao/deployment-strategies-51k8</link>
      <guid>https://dev.to/godinhojoao/deployment-strategies-51k8</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Every time you ship code you have to answer one question: how do the new version&lt;br&gt;
and the old version hand off? Do all users move at once, or a few at a time? Is&lt;br&gt;
there downtime? Can you roll back fast if something breaks?&lt;/p&gt;

&lt;p&gt;Deployment strategies are the different answers to that question. Each one trades&lt;br&gt;
&lt;strong&gt;simplicity&lt;/strong&gt; for some mix of &lt;strong&gt;zero downtime, safer rollout, and better&lt;br&gt;
testing&lt;/strong&gt;, and each extra guarantee costs you complexity and infrastructure.&lt;/p&gt;

&lt;p&gt;This article walks through the main strategies from the simplest to the most&lt;br&gt;
advanced, then ends with a simple flow to pick one: you start at big-bang and&lt;br&gt;
"buy" each new capability with a bit more complexity.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to decouple release and deploy
&lt;/h2&gt;

&lt;p&gt;Traditionally people mix release and deploy and treat them as one single event:&lt;br&gt;
you freeze the code in a branch for tests and validation, then put it into&lt;br&gt;
production available for all clients at once.&lt;/p&gt;

&lt;p&gt;Decoupling them means splitting that single event into two separate things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deploy&lt;/strong&gt; = publishing new code to production infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Release&lt;/strong&gt; = exposing that code to clients, whenever you decide.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbb66onvycymufbogegjp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbb66onvycymufbogegjp.png" alt="Traditional vs decoupled release/deploy" width="799" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can achieve that by using &lt;strong&gt;feature flags&lt;/strong&gt;, a technique that can be combined&lt;br&gt;
with any of the deployment strategies below. A feature flag is a runtime switch (a&lt;br&gt;
config value, not a code change) that decides whether a piece of code actually&lt;br&gt;
runs. You wrap the new behavior in a flag that defaults to "off" and ship the&lt;br&gt;
code, so it sits in production deployed but not released. Nobody sees it until you&lt;br&gt;
flip the flag on, and flipping it off brings the old behavior back instantly (a&lt;br&gt;
"kill switch").&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3i0k2el7njmzri21hw85.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3i0k2el7njmzri21hw85.png" alt="Feature flag decoupling deploy from release" width="800" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image from &lt;a href="https://circleci.com/blog/deployment-strategies-types-trade-offs-and-how-to-choose/" rel="noopener noreferrer"&gt;CircleCI&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flag debt&lt;/strong&gt;: flags pile up and need discipline to clean up once a feature is
fully released. A good practice is to set a deadline for removing each flag so
they don't live in the code forever.&lt;/li&gt;
&lt;li&gt;Extra complexity in the code, since both the old and new paths live side by side
while the flag exists.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Problems it solves:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A deploy stops being risky, because new code can go to production without being
exposed to anyone.&lt;/li&gt;
&lt;li&gt;Rollback is instant: flip the flag off instead of redeploying the old version.
This gives us space to be more innovative and less busy putting out fires like
firemen.&lt;/li&gt;
&lt;li&gt;You can release to whoever you want first (1%, internal users, a single client)
and widen from there.&lt;/li&gt;
&lt;li&gt;The release decision no longer depends on a deploy, so product or ops can pick
the timing instead of waiting on a new build.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Big-bang
&lt;/h2&gt;

&lt;p&gt;Stop all instances of the current version, then start the new one. No traffic&lt;br&gt;
splitting, no coexistence, every user moves at once.&lt;/p&gt;

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

&lt;p&gt;It is simple and fast to implement, which is its whole appeal. The cost is&lt;br&gt;
&lt;strong&gt;downtime&lt;/strong&gt; during the swap (seconds to minutes), high risk, and no easy&lt;br&gt;
rollback: if the new version is broken, every user hits the problem immediately.&lt;/p&gt;

&lt;p&gt;Good for dev/staging, scheduled maintenance windows, and breaking changes where&lt;br&gt;
running two versions at once would be a problem anyway. AWS calls this same idea&lt;br&gt;
&lt;strong&gt;all-at-once&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Recreate
&lt;/h2&gt;

&lt;p&gt;Terminate the previous version completely, then rebuild the whole environment&lt;br&gt;
with the new one. Only one version ever runs.&lt;/p&gt;

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

&lt;p&gt;This looks almost identical to big-bang. Both have downtime, both move everyone&lt;br&gt;
at once, neither runs two versions together. The difference is emphasis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Big-bang&lt;/strong&gt; is about &lt;strong&gt;rollout scope&lt;/strong&gt;: the change goes to everyone at once,
with no phasing. It can happen &lt;em&gt;in place&lt;/em&gt; on the same machine, so you stop the
old process, swap the code, and start the new process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recreate&lt;/strong&gt; is about the &lt;strong&gt;infrastructure&lt;/strong&gt;: destroy the old environment and
rebuild it from a clean image, then deploy onto it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why throw the whole box away? Restarting in place carries over old state (stale&lt;br&gt;
files, cached artifacts, manual tweaks), which drifts your config over time.&lt;br&gt;
Recreate guarantees a clean slate every deploy. It is simple and predictable, but&lt;br&gt;
still has downtime and limited testing before the switch.&lt;/p&gt;
&lt;h2&gt;
  
  
  Rolling
&lt;/h2&gt;

&lt;p&gt;Update instances in &lt;strong&gt;batches&lt;/strong&gt; while the app stays up, so old and new versions&lt;br&gt;
serve traffic at the same time until every instance is on the new version. No&lt;br&gt;
downtime, and you reuse the existing capacity, so no new infrastructure.&lt;/p&gt;

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

&lt;p&gt;The catch is that both versions run at once, so the new version must be&lt;br&gt;
&lt;strong&gt;backward-compatible&lt;/strong&gt; with the old (same database, same API contracts).&lt;br&gt;
Rollback means rolling &lt;em&gt;backward&lt;/em&gt; through the batches.&lt;/p&gt;

&lt;p&gt;The diagram above (and what I'm describing here) is &lt;strong&gt;rolling instance-based&lt;/strong&gt;,&lt;br&gt;
without any traffic-splitting logic: you just swap whole instances one batch at a&lt;br&gt;
time. But there are two common ways to do the rollout:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Instance-based (the traditional):&lt;/strong&gt; swap whole instances one batch at a time.
Simple, since you just need enough instances to roll through, and no logic to
split requests. Each instance either runs the old version or the new one, and
you accept a small dip in capacity during the rollout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traffic-based:&lt;/strong&gt; keep both versions up and use a &lt;strong&gt;load balancer&lt;/strong&gt; to decide
which instance each request hits, shifting the &lt;em&gt;percentage of traffic&lt;/em&gt; toward
the new one, often in equal increments on a timer (AWS calls this &lt;strong&gt;linear&lt;/strong&gt;).
This needs a load balancer or proxy that can split requests, but it handles the
transition more smoothly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rolling is a good default for backward-compatible changes when infra cost matters&lt;br&gt;
and you release often.&lt;/p&gt;
&lt;h2&gt;
  
  
  Canary
&lt;/h2&gt;

&lt;p&gt;Route a small slice of production traffic (typically &lt;strong&gt;1% to 5%&lt;/strong&gt;) to the new&lt;br&gt;
version while everyone else stays on the current one. Watch the metrics; if they&lt;br&gt;
stay healthy, grow the slice until the new version takes over.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Image from &lt;a href="https://circleci.com/blog/deployment-strategies-types-trade-offs-and-how-to-choose/" rel="noopener noreferrer"&gt;CircleCI&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Compared to rolling, canary adds &lt;strong&gt;real-time monitoring and automated analysis&lt;/strong&gt;:&lt;br&gt;
you are not just shifting traffic, you are watching a small blast radius for&lt;br&gt;
errors and rolling back before most users ever see them. That buys minimal&lt;br&gt;
production risk, early bug detection, and real feedback from real users, at a much&lt;br&gt;
lower cost than a full duplicate environment.&lt;/p&gt;

&lt;p&gt;The key difference from rolling is the &lt;strong&gt;rollback&lt;/strong&gt;. Canary watches metrics and,&lt;br&gt;
if they degrade, sends all traffic back to the current version. Rolling has no&lt;br&gt;
metrics gate, so to undo it you roll &lt;em&gt;backward&lt;/em&gt; through the batches, which is&lt;br&gt;
slower and manual.&lt;/p&gt;

&lt;p&gt;The price is the machinery: traffic splitting, monitoring, and enough traffic for&lt;br&gt;
the metrics to mean something. Best for high-traffic services that already have&lt;br&gt;
monitoring in place.&lt;/p&gt;
&lt;h2&gt;
  
  
  Blue/green
&lt;/h2&gt;

&lt;p&gt;Keep &lt;strong&gt;two identical production environments&lt;/strong&gt;. One (blue) serves all traffic;&lt;br&gt;
the other (green) sits idle. Deploy the new version to green, validate it fully,&lt;br&gt;
then switch all traffic over at once. If something breaks, switch back.&lt;/p&gt;

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

&lt;p&gt;The win is &lt;strong&gt;rollback speed&lt;/strong&gt; (under a minute) and full validation of the real&lt;br&gt;
environment before any user touches it, with no downtime and a seamless switch.&lt;br&gt;
The cost is &lt;strong&gt;double compute&lt;/strong&gt;: you pay for two complete environments. A shared&lt;br&gt;
database still needs care (an expand-migrate-contract approach), since both&lt;br&gt;
versions may touch it around the switch. AWS notes this is sometimes called&lt;br&gt;
&lt;strong&gt;red/black&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Reach for it when rollback speed is critical and the budget allows the&lt;br&gt;
duplication.&lt;/p&gt;
&lt;h2&gt;
  
  
  Shadow
&lt;/h2&gt;

&lt;p&gt;Run the new version alongside the current one and &lt;strong&gt;mirror real production&lt;br&gt;
traffic&lt;/strong&gt; to it, but never serve its responses to users. The shadow's output is&lt;br&gt;
logged and compared, then discarded.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Image from &lt;a href="https://circleci.com/blog/deployment-strategies-types-trade-offs-and-how-to-choose/" rel="noopener noreferrer"&gt;CircleCI&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is the safest way to test, because users are never affected while you put the&lt;br&gt;
new version under real production load and uncover real-world issues. The catch is&lt;br&gt;
&lt;strong&gt;stateful operations&lt;/strong&gt;: database writes, payments, emails, anything with side&lt;br&gt;
effects runs &lt;em&gt;twice&lt;/em&gt; unless you filter it out. You also pay for a duplicate&lt;br&gt;
environment plus the request-duplication plumbing.&lt;/p&gt;

&lt;p&gt;Great for model swaps, algorithm rewrites, and validating database migrations&lt;br&gt;
under real load when you cannot tolerate downtime. Feature flags cannot replicate&lt;br&gt;
this, which is what makes it unique.&lt;/p&gt;
&lt;h2&gt;
  
  
  Pros and cons
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;th&gt;Pros&lt;/th&gt;
&lt;th&gt;Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Big-bang&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple and fast; fine for breaking changes (no coexistence)&lt;/td&gt;
&lt;td&gt;Downtime; high risk; no easy rollback; every user hit immediately; new version isn't tested under real traffic before going live&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Recreate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple and predictable; clean slate, single running version&lt;/td&gt;
&lt;td&gt;Downtime; high risk; no easy rollback; every user hit immediately; new version isn't tested under real traffic before going live&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rolling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reuses existing capacity (no new infra); no downtime; issues hit only a subset; old version still there to roll back to&lt;/td&gt;
&lt;td&gt;Needs backward compatibility; rollback means rolling in reverse; traffic-based variant needs a load balancer; more to manage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Canary&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cheaper than blue/green; minimal production risk; early bug detection; real user feedback; easy rollback&lt;/td&gt;
&lt;td&gt;Needs traffic splitting, monitoring, and automated analysis; slower to full release; needs enough traffic for metrics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Blue/green&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No downtime; fast rollback (under a minute); full validation before the switch&lt;/td&gt;
&lt;td&gt;Double compute cost; shared database still needs expand-migrate-contract&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Shadow&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Safest, since users are never affected; full production-load testing; finds real-world issues&lt;/td&gt;
&lt;td&gt;Stateful ops run twice and must be filtered; duplicate environment plus request duplication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Feature flags&lt;/strong&gt; &lt;em&gt;(on top of any of the above)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Decouples deploy from release; instant kill-switch rollback; no extra infra&lt;/td&gt;
&lt;td&gt;Flag debt; needs cleanup discipline; cannot replicate shadow&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  How to choose
&lt;/h2&gt;

&lt;p&gt;Think of it as a ladder. You start simple and only climb when you need the next&lt;br&gt;
capability, and each step costs more complexity and infrastructure than the last.&lt;br&gt;
As pseudocode, cheapest first, adding one guarantee at every step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// start simple; each need bumps you up one step (more complexity, more advantage)&lt;/span&gt;

&lt;span class="nx"&gt;pick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Big&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;bang&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;Recreate&lt;/span&gt;            &lt;span class="c1"&gt;// simplest: accepts downtime, risky&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;needZeroDowntime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;pick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Rolling &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;based&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// + no downtime&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;needSmootherTransition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;pick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Rolling &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;traffic&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;based&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;// + shift by % of traffic&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;needMonitoringOnSmallSlice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;pick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Canary&lt;/span&gt;                     &lt;span class="c1"&gt;// + early detection, auto rollback&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;needFasterRollback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;pick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Blue&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;green&lt;/span&gt;                 &lt;span class="c1"&gt;// + instant switch and rollback&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;needRealLoadTestingWithNoUserRisk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;pick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Shadow&lt;/span&gt;                     &lt;span class="c1"&gt;// + test on real traffic, zero user impact&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Can you accept downtime, and do you ship breaking changes?&lt;/strong&gt; Stop here, at
&lt;strong&gt;big-bang / recreate&lt;/strong&gt;. No coexistence means breaking changes are fine, and
there is nothing to configure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Want zero downtime, but no duplicate environment and no traffic-splitting
logic?&lt;/strong&gt; Climb to &lt;strong&gt;rolling (instance-based)&lt;/strong&gt;. You roll batches of instances and
accept a small capacity dip.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Want that transition to be smoother with traffic-splitting logic?&lt;/strong&gt; Move to
&lt;strong&gt;rolling (traffic-based / linear)&lt;/strong&gt;, shifting traffic by percentage instead of
by whole instances.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Want to catch problems on a small slice with real-time monitoring?&lt;/strong&gt; Step up to
&lt;strong&gt;canary&lt;/strong&gt;: expose 1% to 5%, watch the metrics, and roll back before most users
are affected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Want the fastest possible rollback and can pay for a duplicate environment?&lt;/strong&gt;
Go to &lt;strong&gt;blue/green&lt;/strong&gt;: validate a full second environment, then flip.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Have a duplicate environment and need real production-load testing with no user
risk?&lt;/strong&gt; Top of the ladder is &lt;strong&gt;shadow&lt;/strong&gt;: mirror real traffic, serve none of it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each step trades a bit more complexity for one more guarantee: zero downtime, then&lt;br&gt;
smoother traffic, then early detection, then instant rollback, then risk-free&lt;br&gt;
testing. Pick the lowest step that gives you what you actually need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read also
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/godinhojoao/jenkins-cicd-from-scratch-23h1"&gt;Jenkins CI/CD from scratch&lt;/a&gt;,&lt;br&gt;
so you can learn how to implement these deployment strategies with the help of a&lt;br&gt;
tool that lets you automate deploys and rollbacks easily, creating pipelines with&lt;br&gt;
build, testing, validation and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://circleci.com/blog/deployment-strategies-types-trade-offs-and-how-to-choose/" rel="noopener noreferrer"&gt;CircleCI - Deployment strategies: types, trade-offs, and how to choose&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/whitepapers/latest/introduction-devops-aws/deployment-strategies.html" rel="noopener noreferrer"&gt;AWS - Deployment strategies (Introduction to DevOps on AWS)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://launchdarkly.com/blog/deployment-strategies/" rel="noopener noreferrer"&gt;LaunchDarkly - Deployment strategies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://launchdarkly.com/blog/why-decouple-deployments-from-releases/" rel="noopener noreferrer"&gt;LaunchDarkly - Why decouple deployments from releases?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>computerscience</category>
      <category>infrastructure</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Jenkins CI/CD from scratch</title>
      <dc:creator>João Godinho</dc:creator>
      <pubDate>Tue, 11 Aug 2026 03:53:24 +0000</pubDate>
      <link>https://dev.to/godinhojoao/jenkins-cicd-from-scratch-23h1</link>
      <guid>https://dev.to/godinhojoao/jenkins-cicd-from-scratch-23h1</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Shipping code by hand is slow and easy to get wrong: run the tests, build the&lt;br&gt;
image, copy it to the server, restart, check it still works. CI/CD automates all&lt;br&gt;
of that. You push code, and a pipeline runs those steps for you, every time, the&lt;br&gt;
same way.&lt;/p&gt;

&lt;p&gt;This article explains the basics of Jenkins and walks through a small, real&lt;br&gt;
pipeline that tests, builds, deploys, and load-tests an API, and rolls back on&lt;br&gt;
its own if something breaks. The full code is here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repository: &lt;a href="https://github.com/godinhojoao/jenkins-demo" rel="noopener noreferrer"&gt;https://github.com/godinhojoao/jenkins-demo&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I run this on a homelab: a Raspberry Pi with &lt;a href="https://about.gitea.com/" rel="noopener noreferrer"&gt;Gitea&lt;/a&gt;&lt;br&gt;
(a self-hosted Git server) next to &lt;a href="https://www.jenkins.io/" rel="noopener noreferrer"&gt;Jenkins&lt;/a&gt;. The public repo above uses GitHub so&lt;br&gt;
anyone can reproduce it.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is Jenkins?
&lt;/h2&gt;

&lt;p&gt;Jenkins is an open-source automation server for CI/CD (Continuous Integration and&lt;br&gt;
Continuous Delivery). Instead of running build and deploy commands by hand, you&lt;br&gt;
describe them once and Jenkins runs them on every push.&lt;/p&gt;

&lt;p&gt;A few terms worth knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Controller&lt;/strong&gt;: the main Jenkins process. Hosts the web UI, stores jobs and
config, and schedules work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent (node)&lt;/strong&gt;: a worker that actually runs the build steps. With one
machine, the controller also runs the builds (&lt;code&gt;agent any&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pipeline&lt;/strong&gt;: a job whose steps are code, split into &lt;strong&gt;stages&lt;/strong&gt; (Test, Build,
Deploy, ...).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jenkinsfile&lt;/strong&gt;: the file that defines the pipeline, committed next to the app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trigger&lt;/strong&gt;: what starts a build automatically (a webhook, or polling the repo).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The pipeline
&lt;/h2&gt;

&lt;p&gt;The pipeline in this repo has five stages, and rolls back if anything fails after&lt;br&gt;
a deploy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Test    -&amp;gt; npm test inside a Docker build (broken code never ships)
Build   -&amp;gt; docker build, image tagged by commit
Deploy  -&amp;gt; docker compose up -d
Verify  -&amp;gt; hit /health until it responds
Load    -&amp;gt; k6 sends traffic and checks latency/error thresholds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;strong&gt;Test&lt;/strong&gt; or &lt;strong&gt;Build&lt;/strong&gt; fails, the pipeline stops and nothing is deployed. If&lt;br&gt;
&lt;strong&gt;Verify&lt;/strong&gt; or &lt;strong&gt;Load test&lt;/strong&gt; fails, the new version was already deployed, so a&lt;br&gt;
&lt;code&gt;post { failure }&lt;/code&gt; block redeploys the last good image automatically.&lt;/p&gt;

&lt;p&gt;Here is the whole thing as code (&lt;code&gt;http-api/Jenkinsfile&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;

  &lt;span class="n"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;IMAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'http-api'&lt;/span&gt;
    &lt;span class="n"&gt;TAG&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${env.GIT_COMMIT?.take(7) ?: 'dev'}"&lt;/span&gt;    &lt;span class="c1"&gt;// tag image by commit&lt;/span&gt;
    &lt;span class="n"&gt;STATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${JENKINS_HOME}/http-api.last_good"&lt;/span&gt;      &lt;span class="c1"&gt;// remembers last good tag&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;stages&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Test'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// runs the tests inside the Docker build; fails the run if a test fails&lt;/span&gt;
      &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'http-api'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'docker build --target test -t $IMAGE:test .'&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Build'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'http-api'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'docker build --target runtime -t $IMAGE:$TAG .'&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Deploy'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'http-api'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'IMAGE_TAG=$TAG docker compose up -d'&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Verify'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// is the app up? fail fast before wasting time on the load test&lt;/span&gt;
      &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'http-api'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'''
            for n in $(seq 1 10); do
              if docker compose exec -T app wget -qO- http://localhost:3000/health &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then
                echo healthy; exit 0
              fi
              sleep 2
            done
            echo "health check failed"; exit 1
          '''&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Load test'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// k6 checks latency/error thresholds; a breach fails the stage&lt;/span&gt;
      &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'http-api'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'''
            docker run --rm -i --network host \
              -e BASE_URL=http://localhost:8091 \
              grafana/k6 run - &amp;lt; loadtest.js
            echo $TAG &amp;gt; "$STATE"      # only now is this tag the rollback target
          '''&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;failure&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// auto-rollback: redeploy the last version that passed the load test&lt;/span&gt;
      &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'http-api'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'''
          if [ -f "$STATE" ]; then
            PREV=$(cat "$STATE")
            echo "rolling back to $PREV"
            IMAGE_TAG=$PREV docker compose up -d
          fi
        '''&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a &lt;em&gt;recreate&lt;/em&gt; (big bang) deployment: one container is stopped and a new one&lt;br&gt;
started, so there is a short downtime window during the swap. Strategies like&lt;br&gt;
blue/green or canary remove that gap, but recreate is simple and fine for a&lt;br&gt;
homelab.&lt;/p&gt;
&lt;h2&gt;
  
  
  The job as code (JCasC)
&lt;/h2&gt;

&lt;p&gt;You can create a Jenkins job by clicking through the UI, but this repo defines it&lt;br&gt;
as code with the &lt;strong&gt;Configuration as Code (JCasC)&lt;/strong&gt; and &lt;strong&gt;Job DSL&lt;/strong&gt; plugins. When&lt;br&gt;
the Jenkins container starts, it reads &lt;code&gt;jenkins-server/jenkins.yaml&lt;/code&gt; and creates&lt;br&gt;
the job by itself: the repo URL, the branch, where the Jenkinsfile lives, how&lt;br&gt;
often to poll, and which paths should trigger a build.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;pipelineJob('jenkins-demo') {&lt;/span&gt;
  &lt;span class="s"&gt;definition {&lt;/span&gt;
    &lt;span class="s"&gt;cpsScm {&lt;/span&gt;
      &lt;span class="s"&gt;scm {&lt;/span&gt;
        &lt;span class="s"&gt;git {&lt;/span&gt;
          &lt;span class="s"&gt;remote { url('https://github.com/godinhojoao/jenkins-demo.git') }&lt;/span&gt;
          &lt;span class="s"&gt;branch('*/main')&lt;/span&gt;
          &lt;span class="s"&gt;extensions {&lt;/span&gt;
            &lt;span class="s"&gt;// only build when files under http-api/ change&lt;/span&gt;
            &lt;span class="s"&gt;pathRestriction { includedRegions('http-api/.*') }&lt;/span&gt;
          &lt;span class="s"&gt;}&lt;/span&gt;
        &lt;span class="s"&gt;}&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
      &lt;span class="s"&gt;scriptPath('http-api/Jenkinsfile')&lt;/span&gt;
    &lt;span class="s"&gt;}&lt;/span&gt;
  &lt;span class="s"&gt;}&lt;/span&gt;
  &lt;span class="s"&gt;triggers { scm('* * * * *') }   // poll every minute&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The benefit: the whole setup is reproducible. Rebuild the machine and the job&lt;br&gt;
comes back exactly the same, no manual clicking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build agents
&lt;/h2&gt;

&lt;p&gt;In this homelab I don't use agents: the same Jenkins container runs the UI and&lt;br&gt;
the builds (&lt;code&gt;agent any&lt;/code&gt;). It is simple and fine for one machine and my own code.&lt;br&gt;
In a real setup, though, running builds on separate agents is recommended. The&lt;br&gt;
main gains are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Isolation (security): builds run away from the controller, so they can't reach
its config and credentials. Important for untrusted code, like public pull requests.&lt;/li&gt;
&lt;li&gt;Scale: spread many builds across machines and run them in parallel.&lt;/li&gt;
&lt;li&gt;Clean environments: a fresh, disposable container per build.&lt;/li&gt;
&lt;li&gt;Different platforms: build on another OS or CPU architecture.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup in short
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. push the project to a public GitHub repo&lt;/span&gt;
git init &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git add &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"init"&lt;/span&gt;
git remote add origin https://github.com/&amp;lt;you&amp;gt;/jenkins-demo.git
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main

&lt;span class="c"&gt;# 2. start Jenkins&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;jenkins-server
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--build&lt;/span&gt;
docker compose logs jenkins        &lt;span class="c"&gt;# copy the first-run admin password&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the Jenkins UI, unlock it, install the suggested plugins, and create a user.&lt;br&gt;
The job is created automatically from &lt;code&gt;jenkins.yaml&lt;/code&gt;. From then on, a push runs&lt;br&gt;
the pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it looks like
&lt;/h2&gt;

&lt;p&gt;A successful run: every stage passes and the app is deployed.&lt;/p&gt;

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

&lt;p&gt;A failing test stops the pipeline, so nothing is deployed.&lt;/p&gt;

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

&lt;p&gt;The app deployed but failed the load test, so it rolled back to the last good&lt;br&gt;
image.&lt;/p&gt;

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

&lt;p&gt;A change outside &lt;code&gt;http-api/&lt;/code&gt; (like editing the README) is ignored by polling, so&lt;br&gt;
no build runs.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Running it on your own homelab
&lt;/h2&gt;

&lt;p&gt;I run this on a Raspberry Pi with Gitea and Jenkins side by side. That keeps my&lt;br&gt;
code and my pipelines fully under my control, with no dependency on any cloud.&lt;/p&gt;

&lt;p&gt;In this public demo the job polls GitHub, because GitHub can't reach a machine on&lt;br&gt;
a home network. On my own homelab I don't poll: since Gitea is local, it can&lt;br&gt;
reach Jenkins directly, so a Gitea webhook triggers builds instantly on every&lt;br&gt;
push. In production, with a public IP, a VPS, or a configured tunnel, you could&lt;br&gt;
use GitHub webhooks the same way.&lt;/p&gt;

&lt;p&gt;If you have a homelab, I recommend the same: self-host Gitea, connect it to&lt;br&gt;
Jenkins with a webhook, and everything runs on hardware you own. That is the whole&lt;br&gt;
point of a homelab, keeping your code and pipeline private and in your hands.&lt;/p&gt;

&lt;p&gt;The full working example, all the configs, and a step-by-step setup are in the&lt;br&gt;
repository: &lt;a href="https://github.com/godinhojoao/jenkins-demo" rel="noopener noreferrer"&gt;https://github.com/godinhojoao/jenkins-demo&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.jenkins.io/doc/book/pipeline/" rel="noopener noreferrer"&gt;Jenkins Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.jenkins.io/projects/jcasc/" rel="noopener noreferrer"&gt;Jenkins Configuration as Code (JCasC)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://plugins.jenkins.io/job-dsl/" rel="noopener noreferrer"&gt;Job DSL plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://about.gitea.com/" rel="noopener noreferrer"&gt;Gitea&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://grafana.com/docs/k6/latest/" rel="noopener noreferrer"&gt;k6 load testing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>homelab</category>
      <category>computerscience</category>
      <category>jenkins</category>
    </item>
    <item>
      <title>Heap Data Structure</title>
      <dc:creator>João Godinho</dc:creator>
      <pubDate>Sun, 10 May 2026 01:06:30 +0000</pubDate>
      <link>https://dev.to/godinhojoao/heap-data-structure-3dkh</link>
      <guid>https://dev.to/godinhojoao/heap-data-structure-3dkh</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This post covers binary heaps as a complete binary tree stored in an array, where each parent follows a priority rule (min-heap or max-heap). It explains the core operations &lt;code&gt;push&lt;/code&gt; (heapifyUp) and &lt;code&gt;pop&lt;/code&gt; (heapifyDown), the heap property, and provides an implementation.&lt;/li&gt;
&lt;li&gt;It also covers trade-offs of using heaps as priority queues, real use in greedy algorithms like Dijkstra’s algorithm and Huffman coding, O(n) in-place heap construction, plus heapsort, and a discussion about introsort.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Binary Heap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A binary heap is a special case of a heap with the following properties:

&lt;ol&gt;
&lt;li&gt;It is a complete binary tree (every level is full except the last, which is filled from left to right).&lt;/li&gt;
&lt;li&gt;Being a complete binary tree implies:

&lt;ul&gt;
&lt;li&gt;If a node is at index &lt;code&gt;i&lt;/code&gt;:&lt;/li&gt;
&lt;li&gt;Left child = &lt;code&gt;2*i + 1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Right child = &lt;code&gt;2*i + 2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Parent = &lt;code&gt;(i - 1) / 2&lt;/code&gt; (if not root)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Heap property (what differentiates heaps):

&lt;ul&gt;
&lt;li&gt;The parent has higher priority than its children&lt;/li&gt;
&lt;li&gt;Example: min-heap (highest priority = smallest value)&lt;/li&gt;
&lt;li&gt;Parent ≤ both children&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;/li&gt;

&lt;li&gt;As you can see, the parent always has the highest priority.&lt;/li&gt;

&lt;li&gt;This implies that the root (first node) always has the highest priority.&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  K-ary Heap
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We could have a &lt;strong&gt;k-ary heap&lt;/strong&gt;, the properties stay the same, only changes are:

&lt;ul&gt;
&lt;li&gt;If a node is at index &lt;code&gt;i&lt;/code&gt;:&lt;/li&gt;
&lt;li&gt;first child = &lt;code&gt;k*i + 1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;second child = &lt;code&gt;k*i + 2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;k-th child = &lt;code&gt;k*i + k&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Parent = &lt;code&gt;(i - 1) / k&lt;/code&gt; (if not root)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pseudocode for Binary Heap → MinHeap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;We save heap items into an array: &lt;code&gt;items = []&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;1. &lt;code&gt;PUSH&lt;/code&gt; inserts new nodes

&lt;ul&gt;
&lt;li&gt;a. Insert at end of array &lt;code&gt;items.push(newItem), addedItemIndex = i&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;b. Now if we have more than 1 element we have to check property validity:

&lt;ul&gt;
&lt;li&gt;i. parent ≤ child&lt;/li&gt;
&lt;li&gt;ii. if true → valid, do nothing&lt;/li&gt;
&lt;li&gt;iii. if false → invalid, &lt;code&gt;swap(parent, child)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;iv. &lt;strong&gt;repeat until reach valid heap state.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;We generally call this &lt;code&gt;b step&lt;/code&gt; of &lt;strong&gt;fixing the tree&lt;/strong&gt; when inserting as &lt;strong&gt;&lt;code&gt;heapifyUp&lt;/code&gt;.&lt;/strong&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;heapifyUp&lt;/code&gt; because we start from the bottom (after adding at the end of the heap), and &lt;strong&gt;fix the heap bottom-to-top&lt;/strong&gt; by &lt;strong&gt;comparing the node with its parent.&lt;/strong&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;2. &lt;code&gt;POP&lt;/code&gt; get root (highest priority element)

&lt;ul&gt;
&lt;li&gt;a. Remove root node and put the last added on its place.&lt;/li&gt;
&lt;li&gt;b. Now if we have more than 1 element we have to check property validity:

&lt;ul&gt;
&lt;li&gt;i. parent ≤ both children&lt;/li&gt;
&lt;li&gt;ii. if true → valid, do nothing&lt;/li&gt;
&lt;li&gt;iii. if false → invalid, &lt;code&gt;swap(parent, smallestChild)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;iv. &lt;strong&gt;repeat until reach valid heap state.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Here we call it &lt;code&gt;heapifyDown&lt;/code&gt; since we start on root and go down, comparing the parent with its children.&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Binary Heap Code
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/godinhojoao/dsa-studies/blob/main/classwork/huffman/huffman.c" rel="noopener noreferrer"&gt;Huffman Code in C&lt;/a&gt; - contains minHeap implementation within it&lt;/li&gt;
&lt;li&gt;JS implementation below: &lt;a href="https://github.com/godinhojoao/dsa-studies/blob/main/dsa-in-js/heap1-binary-heap.js" rel="noopener noreferrer"&gt;&lt;strong&gt;Github repository&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BinaryHeap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;hasHigherPriorityFn&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hasHigherPriorityFn&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'items' field, is required&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// no types here in JS, so we trust hasHigherPriorityFn function and items array -&amp;gt; the focus is binary heap&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;hasHigherPriorityFn&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;hasHigherPriorityFn&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'hasHigherPriorityFn' function is required&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;hasHigherPriorityFn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hasHigherPriorityFn&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;buildHeapInPlace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addedIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parentIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;temp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;addedIndex&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;addedIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;parentIndex&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;parentIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// time: O(log n), space O(log n) -&amp;gt; recursion, iterative is O(1) space&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;heapifyUp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;insertedItemCurrIndex&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parentIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;insertedItemCurrIndex&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parentIndex&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;insertedItemCurrIndex&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;parentIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;insertedItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;insertedItemCurrIndex&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shouldSwap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;hasHigherPriorityFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;insertedItem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;parentIndex&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shouldSwap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;insertedItemCurrIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parentIndex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;heapifyUp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;insertedItemCurrIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parentIndex&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// time: O(log n), space O(log n) -&amp;gt; recursion, iterative is O(1) space&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;heapifyDown&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;currItemPosition&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;mostPriorityItemIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currItemPosition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;leftChildIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;currItemPosition&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rightChildIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;currItemPosition&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;leftChildExists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;leftChildIndex&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;leftChildExists&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;hasHigherPriorityFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;leftChildIndex&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mostPriorityItemIndex&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;mostPriorityItemIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;leftChildIndex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rightChildExists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rightChildIndex&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rightChildExists&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;hasHigherPriorityFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;rightChildIndex&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mostPriorityItemIndex&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;mostPriorityItemIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rightChildIndex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// currItem = parent (start at index 0)&lt;/span&gt;
    &lt;span class="c1"&gt;// if parent already has the highest priority, do nothing&lt;/span&gt;
    &lt;span class="c1"&gt;// otherwise swap with highest priority child until heap is valid again&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mostPriorityItemIndex&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nx"&gt;currItemPosition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currItemPosition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mostPriorityItemIndex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;heapifyDown&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;currItemPosition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mostPriorityItemIndex&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// O(n) because most heapifyDown calls run on small-height nodes.&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;buildHeapInPlace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arrSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arrSize&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;heapifyDown&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;currItemPosition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// O(log n)&lt;/span&gt;
  &lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;heapifyUp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;insertedItemCurrIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// O(log n)&lt;/span&gt;
  &lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currLength&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;itemToReturn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;currLength&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// last item is new root&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// remove last item from array&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currLength&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;heapifyDown&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;currItemPosition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// fix top down&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;itemToReturn&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// O(1)&lt;/span&gt;
  &lt;span class="nf"&gt;peek&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;printItems&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;this.#items&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;consumeAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;consumeAll:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;this.#hasHigherPriorityFn.toString()&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;hasHigherPriorityFn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// smallest item at root index = 0&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MinHeap&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BinaryHeap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;hasHigherPriorityFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;itemB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;itemA&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;itemB&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// biggest item at root index = 0&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MaxHeap&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BinaryHeap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;hasHigherPriorityFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;itemB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;itemA&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;itemB&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--------------------------------------------------------&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;minHeapTest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MinHeap&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;minHeapTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;printItems&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;minHeapTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;consumeAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--------------------------------------------------------&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;maxHeap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MaxHeap&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;maxHeap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;printItems&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;maxHeap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;consumeAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--------------------------------------------------------&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;people&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;João&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Turing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ada Lovelace&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;36&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elderlyPeopleFirstHeap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BinaryHeap&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;people&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;hasHigherPriorityFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;elderlyPeopleFirstHeap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;consumeAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--------------------------------------------------------&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;OUTPUT:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  Time Complexity
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Time complexity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Push&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(log n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Peek&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Pop&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(log n)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;O(log n)&lt;/code&gt;  → Because complete binary tree contains:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;2^height ~ n&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;height = log n&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;It means that every time we fix the heap, the height decreases by one level at each step, and in the worst case it requires moving a node from the bottom (height = 0) to the top (height = log n), which is &lt;code&gt;O(log n)&lt;/code&gt; &lt;strong&gt;in the worst case.&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Or the opposite direction, from top to bottom.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use cases and trade-offs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Commonly used to implement &lt;strong&gt;priority queues&lt;/strong&gt;. Priority queues can also be implemented using other data structures such as arrays.

&lt;ul&gt;
&lt;li&gt;But with a sorted &lt;strong&gt;array&lt;/strong&gt;, for example, you would have &lt;code&gt;O(1) pop&lt;/code&gt; and &lt;code&gt;O(n) insert&lt;/code&gt;, since inserting a new item in the correct position may require shifting all items in the worst case.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;To implement a priority queue&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Use a queue if you will set up all inserts at once and then only perform reads.&lt;/li&gt;
&lt;li&gt;Use a binary heap or k-ary heap if you require &lt;strong&gt;faster inserts&lt;/strong&gt; &lt;code&gt;O(log n)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Widely used in greedy algorithms that require extracting the highest priority item at each step from a priority queue:

&lt;ul&gt;
&lt;li&gt;Huffman coding (greedy + min-heap) - &lt;a href="https://github.com/godinhojoao/dsa-studies/blob/main/classwork/huffman/huffman.c" rel="noopener noreferrer"&gt;C code here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Dijkstra’s algorithm (greedy + min-heap) - &lt;a href="https://github.com/godinhojoao/NavPelotas/blob/main/src/dijkstra.cpp" rel="noopener noreferrer"&gt;C++ code here&lt;/a&gt;, developed by &lt;a href="https://github.com/DevTheusP" rel="noopener noreferrer"&gt;Matheus Persch&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;And more…&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  In place &lt;code&gt;O(n)&lt;/code&gt; build max heap instead of &lt;code&gt;O(n * log n)&lt;/code&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/godinhojoao/dsa-studies/blob/main/dsa-in-js/heap2-build-heap-in-place.js" rel="noopener noreferrer"&gt;&lt;strong&gt;Github Repository&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// theory: all items after n/2 are leaves: A[n/2+1, ..., n]&lt;/span&gt;
&lt;span class="c1"&gt;// practice: as indexes start at 0, n/2 is already a leaf&lt;/span&gt;
&lt;span class="c1"&gt;// so we start at (n/2)-1 until 0&lt;/span&gt;
    &lt;span class="c1"&gt;// --&amp;gt; from height 1 until root&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildHeapInPlace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arrSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arrSize&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;heapifyDown&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;currItemPosition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;How do we know that items after n/2 are leaves?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1. We know that leaves have no children

&lt;ul&gt;
&lt;li&gt;a. left child = &lt;code&gt;2*i+1&lt;/code&gt; ; right child = &lt;code&gt;2*i+2&lt;/code&gt; &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;2. If has no children: &lt;code&gt;2*i &amp;gt; n&lt;/code&gt; 2*i outside array bound &lt;code&gt;n&lt;/code&gt; so there is no children

&lt;ul&gt;
&lt;li&gt;a. And then we find: &lt;code&gt;i &amp;gt; n/2&lt;/code&gt; , so &lt;strong&gt;all&lt;/strong&gt; &lt;strong&gt;items&lt;/strong&gt; &lt;code&gt;A[(n/2)+1, …, n]&lt;/code&gt; &lt;strong&gt;are leaves&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Why do we start from &lt;code&gt;height=1&lt;/code&gt; and not from leaves at &lt;code&gt;height = 0&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;index = n/2, height 1 = first non leaf node;&lt;/li&gt;
&lt;li&gt;Because leaves (h=0) are already valid heaps (don't require fix), and heapifyDown only works to fix a parent with valid heaps as children.&lt;/li&gt;
&lt;li&gt;In a heap, each operation &lt;strong&gt;fixes one node at a time.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Why do we go until &lt;code&gt;height=log n&lt;/code&gt; ?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;height = 1&lt;/code&gt; → first non-leaf node&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;height = log n&lt;/code&gt; → root&lt;/li&gt;
&lt;li&gt;We fix the heap bottom-up.&lt;/li&gt;
&lt;li&gt;When we reach &lt;code&gt;height = log n&lt;/code&gt;, we have fixed the root, so the entire tree is a valid heap.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why is it &lt;strong&gt;O(n) and not O(n *log n)?&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Both &lt;strong&gt;heapifyDown&lt;/strong&gt; and &lt;strong&gt;heapifyUp&lt;/strong&gt; have complexity of &lt;code&gt;O(log n)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;heapifyDown(node)&lt;/code&gt;: max number of required movements = distance from node to the bottom of the tree (expensive if nodes are at the top)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;heapifyUp(node)&lt;/code&gt;: max number of required movements = distance from node to the top of the tree (expensive if nodes are at the bottom)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Given an array, what would you prefer? Start building a heap from top or bottom?&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Starting at top&lt;/strong&gt;: use &lt;code&gt;heapifyUp()&lt;/code&gt; in all nodes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Starting at bottom&lt;/strong&gt;: use &lt;code&gt;heapifyDown()&lt;/code&gt; in all nodes&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Building the sum for each approach to analyze it better
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;At height=0 we have n/2 nodes, at height=1 n/4, and so on…&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;moves per level = &lt;code&gt;(nodes at that height) * (max movements each node can do)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Starting at top with &lt;code&gt;heapifyUp()&lt;/code&gt;, the sum of movements/operations would be:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;tree height = h = log n&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(n/2 * h) + (n/4 * (h-1)) + (n/8 * (h-2)) + ... + (1 * 0)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;But starting at bottom with &lt;code&gt;heapifyDown()&lt;/code&gt;, the sum of operations is:&lt;/strong&gt;&lt;/li&gt;

&lt;li&gt;&lt;code&gt;(n/2 * 0) + (n/4 * 1) + (n/8 * 2) + ... + (1 * h)&lt;/code&gt;&lt;/li&gt;

&lt;li&gt;Compare item by item in the both sums, you will notice, for example, that:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;n/2 * logn &amp;gt; n/2 * 0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;And starting at top makes much more movements for each level.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;But we still haven't answered: &lt;code&gt;why starting at bottom makes it O(n)?&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;If you have at least basic calculus knowledge it will be easier, but I'll not make a formal demonstration, only show an intuitive explanation. &lt;code&gt;k = current level&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;The sum &lt;strong&gt;doesn't depend on &lt;code&gt;n&lt;/code&gt;  it converges to a constant.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;So it would be n*O(1) → O(n)&lt;/li&gt;
&lt;li&gt;In summary, many nodes do small work, and few nodes do large work. The balance gives a linear total &lt;code&gt;O(n)&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;leaf nodes = 0 moves&lt;/li&gt;
&lt;li&gt;level 1 (above leaves) = at most 1 move&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;li&gt;root = at most log n moves&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;For more: watch starting at 30:00 - &lt;a href="https://www.youtube.com/watch?v=B7hVxCmfPtM" rel="noopener noreferrer"&gt;MIT OpenCourseWare - Lecture 4: Heaps and Heap Sort (Srini Devadas, 2011)&lt;/a&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use case for build heap in-place
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If you already have an array, it is better to build the heap in-place using bottom-up heap construction than inserting items one by one and applying heapifyDown multiple times.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;buildInPlaceMaxHeap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// O(n)&lt;/span&gt;

&lt;span class="c1"&gt;// O(n*log n)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// O(n)&lt;/span&gt;
    &lt;span class="nx"&gt;existentMaxHeap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// O(log n)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// When to use for (item of arr) existentMaxHeap.insert(item)?&lt;/span&gt;
&lt;span class="c1"&gt;// 1. you don't receive the entire array at once, just streaming single items&lt;/span&gt;
&lt;span class="c1"&gt;// 2. you already have a big existent heap&lt;/span&gt;
    &lt;span class="c1"&gt;// let's say your heap contains 10k items&lt;/span&gt;
    &lt;span class="c1"&gt;// and you receive 10 items at once&lt;/span&gt;
    &lt;span class="c1"&gt;// it isn't a good idea to rebuild the entire heap again&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Heapsort
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I hope you can read that. Good luck.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7pl23925prddqfh3w7gp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7pl23925prddqfh3w7gp.png" alt="handwritten notes about heapsort with my awkward handwriting" width="800" height="563"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Just kidding, I'll write it down here:&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;1. Build a &lt;strong&gt;MaxHeap&lt;/strong&gt; in &lt;code&gt;O(n)&lt;/code&gt; in place using an array.

&lt;ul&gt;
&lt;li&gt;a. The &lt;strong&gt;heap area&lt;/strong&gt; starts from &lt;code&gt;0&lt;/code&gt; to &lt;code&gt;n-1&lt;/code&gt;. (the entire array is a heap)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;2. Swap the ROOT and the last item, then decrease the heap area by one.

&lt;ul&gt;
&lt;li&gt;a. The root was the biggest element (inside the heap area).&lt;/li&gt;
&lt;li&gt;b. Now the biggest element is at the end (the correct place in the sorted array).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;3. Heapify Down to fix the ROOT.&lt;/li&gt;

&lt;li&gt;4. &lt;code&gt;if (heapArea &amp;gt; 1) { return to step 2 } else { HALT }&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;a. In the handwritten note I put &lt;code&gt;heapArea &amp;gt; 0&lt;/code&gt;, but if only one element remains, this is the smallest one. (The paper is quite wrong.)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;I really suggest you to watch the last minutes starting at 48:00 - &lt;a href="https://www.youtube.com/watch?v=B7hVxCmfPtM" rel="noopener noreferrer"&gt;MIT OpenCourseWare - Lecture 4: Heaps and Heap Sort (2011)&lt;/a&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Heapsort code
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time complexity&lt;/strong&gt;: &lt;code&gt;O(n * log n)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Space complexity&lt;/strong&gt;: &lt;code&gt;O(1) → iterative version&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/godinhojoao/dsa-studies/blob/main/dsa-in-js/heap3-heapsort.js" rel="noopener noreferrer"&gt;&lt;strong&gt;Github repository&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;heapsort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// O(n*log n)&lt;/span&gt;
  &lt;span class="nf"&gt;buildHeapInPlace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;heapAreaEnd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// while runs n-1 times = O(n)&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;heapAreaEnd&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;heapAreaEnd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;heapAreaEnd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// O(log n)&lt;/span&gt;
    &lt;span class="nf"&gt;iterativeHeapifyDown&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;currItemPosition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lastHeapIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;heapAreaEnd&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="c1"&gt;// now heapifyDown receives the lastHeapIndex, to change only heapArea&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// return items; // not needed since heapsort is in place&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;before: arr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// [ 1, 2, 9, 4, 5, 6 ]&lt;/span&gt;
&lt;span class="nf"&gt;heapsort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;after arr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// [ 1, 2, 4, 5, 6, 9 ]&lt;/span&gt;

&lt;span class="c1"&gt;// simplifying: get biggest item (root) and put it at the end of heap area&lt;/span&gt;
&lt;span class="c1"&gt;// now the last item is in the correct position&lt;/span&gt;
&lt;span class="c1"&gt;// --&amp;gt; fix new root that breaks heap property, heapifyDown()&lt;/span&gt;
&lt;span class="c1"&gt;// repeat until all items are in the correct position&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Heapsort is an unstable sort:&lt;/strong&gt;
&lt;/h3&gt;

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

&lt;h3&gt;
  
  
  Heapsort and Merge sort comparison
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;If heapsort has the same Big-O time complexity as merge sort, why is merge sort generally faster?

&lt;ul&gt;
&lt;li&gt;merge sort: time &lt;code&gt;O(n*log n)&lt;/code&gt;, space &lt;code&gt;O(n)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory locality:&lt;/strong&gt; heapsort jumps around memory (&lt;code&gt;left child = 2*i+1&lt;/code&gt;, ...) while merge sort uses sequential memory, taking advantage of cache locality and CPU cache.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comparisons and data movements:&lt;/strong&gt; heapsort needs to repeatedly compute left and right children, compare multiple nodes using the max heap &lt;code&gt;a &amp;gt; b&lt;/code&gt; property, and swap elements. These are all constant factors that are not counted in Big-O notation, but in practice they matter.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  EXTRA: Introsort and the use of heapsort
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Quicksort&lt;/strong&gt; is known for being one of the fastest non-hybrid comparison sorting algorithms.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;But there is no one size fits all in computer science. Some standard libs such as in c++ mix multiple sort algorithms for reaching a better hybrid sorting algorithm with best performance.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Algorithm&lt;/th&gt;
&lt;th&gt;Time (Average)&lt;/th&gt;
&lt;th&gt;Time (Worst)&lt;/th&gt;
&lt;th&gt;Space (Average)&lt;/th&gt;
&lt;th&gt;Space (Worst)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Quicksort&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(n log n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(n^2)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(log n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(n)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Introsort&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(n log n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(n log n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(log n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;O(log n)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Introsort is a hybrid sorting algorithm&lt;/strong&gt; that uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;insertion sort&lt;/code&gt; for small arrays&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;quicksort&lt;/code&gt; until a maximum recursion stack depth is reached&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;heapsort&lt;/code&gt; after reaching the maximum recursion stack depth

&lt;ul&gt;
&lt;li&gt;Why not merge sort, since it is better than heapsort? Because heapsort is in-place, so we can continue the work that quicksort has already done.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Pseudocode:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nc"&gt;Introsort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nx"&gt;depth_limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;log2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;introsort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;depth_limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;introsort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;depth_limit&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;// small array -&amp;gt; insertion sort&lt;/span&gt;
        &lt;span class="nf"&gt;insertion_sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;depth_limit&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;// reached recursion depth limit -&amp;gt; heapsort&lt;/span&gt;
        &lt;span class="nf"&gt;heapsort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

  &lt;span class="c1"&gt;// fallback: quicksort&lt;/span&gt;
    &lt;span class="nx"&gt;pivot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;partition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// using median-of-three&lt;/span&gt;
    &lt;span class="nf"&gt;introsort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;pivot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;depth_limit&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;introsort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;pivot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;depth_limit&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=B7hVxCmfPtM" rel="noopener noreferrer"&gt;MIT OpenCourseWare - Lecture 4: Heaps and Heap Sort (Srini Devadas, 2011)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/9755721/how-can-building-a-heap-be-on-time-complexity" rel="noopener noreferrer"&gt;Stack Overflow - How can building a heap be O(n) time complexity?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.geeksforgeeks.org/dsa/k-ary-heap/" rel="noopener noreferrer"&gt;GeeksforGeeks - K-ary Heap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/godinhojoao/dsa-studies/blob/main/classwork/huffman/huffman.c" rel="noopener noreferrer"&gt;Huffman Coding - Github Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/godinhojoao/NavPelotas/blob/main/src/dijkstra.cpp" rel="noopener noreferrer"&gt;Dijkstra - Github Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/godinhojoao/dsa-studies/blob/main/dsa-in-js/heap1-binary-heap.js" rel="noopener noreferrer"&gt;Binary Heap Implementation - Github Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/godinhojoao/dsa-studies/blob/main/dsa-in-js/heap2-build-heap-in-place.js" rel="noopener noreferrer"&gt;Build Heap In Place - Github Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/godinhojoao/dsa-studies/blob/main/dsa-in-js/heap3-heapsort.js" rel="noopener noreferrer"&gt;Heapsort Implementation - Github Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Huffman_coding" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/Huffman_coding&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/Dijkstra's_algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://webpages.charlotte.edu/rbunescu/courses/ou/cs4040/introsort.pdf" rel="noopener noreferrer"&gt;David R. Musser, &lt;em&gt;Introspective Sorting and Selection Algorithms&lt;/em&gt;, Computer Science Department, Rensselaer Polytechnic Institute, Troy, NY.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.geeksforgeeks.org/dsa/introsort-cs-sorting-weapon/" rel="noopener noreferrer"&gt;GeeksforGeeks - Introsort - C++’s Sorting Weapon&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>computerscience</category>
      <category>algorithms</category>
      <category>programming</category>
    </item>
    <item>
      <title>TSP - Travelling Salesman Problem</title>
      <dc:creator>João Godinho</dc:creator>
      <pubDate>Wed, 06 May 2026 01:23:39 +0000</pubDate>
      <link>https://dev.to/godinhojoao/tsp-travelling-salesman-problem-94e</link>
      <guid>https://dev.to/godinhojoao/tsp-travelling-salesman-problem-94e</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I will talk about P vs NP, NP-complete, and NP-hard, define heuristics in computer science, Hamiltonian cycles, TSP and Metric TSP, approximation algorithms, and optimal vs suboptimal solutions. I will also mention local minima, combinatorial explosion, and why exact solutions become infeasible. Finally, we will see an optimal solution and also an approximation algorithm.&lt;/li&gt;
&lt;li&gt;I’ll also discuss the limitations of the chosen approximation algorithm, what could be improved, and mention other options like MST-based solutions, k-opt methods, and metaheuristics such as genetic algorithms, and their trade-offs.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://github.com/godinhojoao/TSP-Approximation-Algorithm" rel="noopener noreferrer"&gt;project code&lt;/a&gt; and analysis were developed in collaboration with &lt;a href="https://www.linkedin.com/in/matheus-persch/" rel="noopener noreferrer"&gt;Matheus Persch&lt;/a&gt; as part of the DSA III class at &lt;em&gt;Federal University of Pelotas (UFPel)&lt;/em&gt;, Brazil.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Types of problems
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Decision problem:&lt;/strong&gt; You want to answer &lt;strong&gt;yes or no.&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;E.g. “Is there any Hamiltonian cycle in this graph G?” (YES/NO)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search problem:&lt;/strong&gt; You want to find a valid solution (if it exists).

&lt;ul&gt;
&lt;li&gt;E.g. “Find a Hamiltonian cycle in this graph G.”&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimization problem:&lt;/strong&gt; You want to find the &lt;strong&gt;best solution according to some objective (min/max)&lt;/strong&gt;.

&lt;ol&gt;
&lt;li&gt;“Find the shortest Hamiltonian cycle in this graph G.” (TSP) - &lt;strong&gt;minimization problem&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;“Find the &lt;strong&gt;maximum clique&lt;/strong&gt; in graph G.” - maximization&lt;/li&gt;
&lt;li&gt;clique = &lt;strong&gt;subset of vertices&lt;/strong&gt; in a graph &lt;strong&gt;that forms a&lt;/strong&gt; &lt;strong&gt;complete subgraph&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Time complexity class&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Informal definition: S&lt;/strong&gt;et of problems solvable in O(TIME(n)) time.

&lt;ul&gt;
&lt;li&gt;Example: sorting is in P because it has polynomial-time algorithms.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Formal definition:&lt;/strong&gt; TIME(t(n)) is the set of languages decidable by a deterministic Turing machine in O(t(n)) time.

&lt;ul&gt;
&lt;li&gt;Note: “decidable” is formal for languages; for problems like sorting, this feels unnatural, so focus on the informal version for now.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  P, NP, NP-Complete, NP-Hard
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Polynomial time (P):&lt;/strong&gt; Those problems which are decidable in polynomial time using a deterministic Turing machine.

&lt;ul&gt;
&lt;li&gt;TIME(O(n^k)) → k constant&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nondeterministic Polynomial time (NP):&lt;/strong&gt; Those decision problems whose solutions can be verified in polynomial time by a deterministic Turing machine.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Verification:&lt;/strong&gt; Given a candidate solution (&lt;strong&gt;certificate&lt;/strong&gt;), we &lt;strong&gt;check if it is valid&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;E.g. “Given a graph and a certificate, check if it is a valid Hamiltonian cycle.”&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NP-Complete (NPC):&lt;/strong&gt;

&lt;ol&gt;
&lt;li&gt;is NP&lt;/li&gt;
&lt;li&gt;Every problem in NP can be reduced to it in polynomial time

&lt;ul&gt;
&lt;li&gt;One example is the &lt;a href="https://en.wikipedia.org/wiki/Boolean_satisfiability_problem" rel="noopener noreferrer"&gt;SAT&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NP-Hard:&lt;/strong&gt; Problems that are at least as hard as the hardest problems in NP.

&lt;ol&gt;
&lt;li&gt;Every problem in NP can be reduced to them in polynomial time&lt;/li&gt;
&lt;li&gt;Not required to be in NP

&lt;ol&gt;
&lt;li&gt;ALL NP-complete are NP-hard&lt;/li&gt;
&lt;li&gt;But not all NP-hard problems are NP-complete&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;NP-hard problems are &lt;strong&gt;not restricted to be decision problems such as P, NP and NPC.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Example: &lt;strong&gt;TSP (optimization version)&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;For more detailed explanations, read the chapters of Michael Sipser’s book or watch his lectures in the references of this post.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Image reference: &lt;a href="https://en.wikipedia.org/wiki/NP-hardness" rel="noopener noreferrer"&gt;Wikipedia NP-hardness&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is the Traveling Salesman problem?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hamiltonian cycle Problem =&lt;/strong&gt; Does a cycle exist that visits every vertex exactly once?

&lt;ul&gt;
&lt;li&gt;Note: you can't repeat the same edge, but there are variations where you can do it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TSP&lt;/strong&gt; = Among all Hamiltonian cycles, which one has minimum cost?&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  Metric TSP
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Metric TSP is formulated on a &lt;strong&gt;complete graph&lt;/strong&gt; with distances satisfying the triangle inequality.&lt;/li&gt;
&lt;li&gt;Metric TSP contains the following properties:

&lt;ul&gt;
&lt;li&gt;nodes= (x,y,z); distance = d&lt;/li&gt;
&lt;li&gt;d(x,y) ≥ 0 (not negative)&lt;/li&gt;
&lt;li&gt;d(x,y) = d(y,x) (symmetric → undirected)&lt;/li&gt;
&lt;li&gt;d(x,y) + d(y,z) ≥ d(x,z) (triangle inequality)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Image reference: &lt;a href="https://www.onlinemathlearning.com/triangle-inequality.html" rel="noopener noreferrer"&gt;https://www.onlinemathlearning.com/triangle-inequality.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;This ensures that for a path a -&amp;gt; b -&amp;gt; c, replacing it with the direct edge a -&amp;gt; c will never increase the cost. It will stay the same or decrease.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why finding the optimal TSP solution is infeasible in practice&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Because it is NP-hard, it means there is no known polynomial-time solution for it.&lt;/li&gt;
&lt;li&gt;There are multiple exact (optimal) algorithms, but brute force is the simplest and has time complexity of O(n!)

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Example: n = 40  n! ~= 8.1591528 * 10^47 ~= 10^47 tours required&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Imagine you can compute 1 billion tours per second: 10^9 tours&lt;/li&gt;
&lt;li&gt;10^47 / 10^9 = 10^38 seconds ~= 3 * 10^30 years required to run it&lt;/li&gt;
&lt;li&gt;age of universe ^= 13.8 * 10^10 years&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It would take far longer than the age of the universe.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;What I’ve shown above is called “combinatorial explosion”.

&lt;ul&gt;
&lt;li&gt;For first city you have &lt;strong&gt;n options&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;For second you have &lt;strong&gt;n-1 options&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;And so on&lt;/li&gt;
&lt;li&gt;It is factorial: n * (n - 1) * (n - 2) * ... * 1 = n!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to solve something that requires this amount of time?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;As mentioned, it would require literally more time than is possible in this universe for a small n. To solve this type of problem, one possible approach is using &lt;strong&gt;approximation algorithms&lt;/strong&gt;, which will provide you an approximate solution instead of the optimal one.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Heuristics in Computer Science
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://link.springer.com/chapter/10.1007/978-3-662-05269-3_6" rel="noopener noreferrer"&gt;Hromkovič, J. (2004), Chapter 6&lt;/a&gt;, defines: ‘A heuristic algorithm in a very general sense is a consistent algorithm for an optimization problem that is based on some transparent (usually simple) strategy of searching in the set of all feasible solutions, and that does not guarantee finding any optimal solution’.&lt;/li&gt;
&lt;li&gt;They must be much faster than the optimal solution, otherwise you would choose the optimal one.&lt;/li&gt;
&lt;li&gt;Types of heuristics:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Constructive&lt;/strong&gt;: Build a solution following a set of pre defined rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improvement&lt;/strong&gt;: Start with a feasible solution (any one), and apply successive small changes to improve it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compound&lt;/strong&gt;: Use both, constructive solution and then an improvement phase.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Approximation algorithms
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If an optimization problem is NP-hard such as the TSP one, there is no polynomial-time algorithm to solve it, unless P=NP (something we don’t know yet).

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Optimal solution&lt;/strong&gt; = the best solution using a non-approximation algorithm, for example, in TSP using brute force to test all possible tours would provide us for sure &lt;strong&gt;the cheapest Hamiltonian cycle&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Suboptimal solution&lt;/strong&gt; = an approximate solution, isn’t the optimal one, but is close to it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;What an approximation algorithm does is exactly this: instead of trying to get an optimal solution for an NP-hard problem until the universe ends, we use an approximation algorithm to achieve an approximate solution with much faster execution time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;k-approximate approximation algorithm =&lt;/strong&gt; Means that it is no worse than k times the optimal solution.

&lt;ul&gt;
&lt;li&gt;for minimization: &lt;strong&gt;optimal = 1;&lt;/strong&gt; &lt;strong&gt;2-approximate = 2 (2*opt)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;for maximization: &lt;strong&gt;optimal = 2;&lt;/strong&gt; &lt;strong&gt;2-approximate = 1 (1/2*opt)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;As you can see, an approximation algorithm is never better than the optimal; otherwise it would be the optimal one.&lt;/li&gt;
&lt;li&gt;It can even be equal to the optimal solution in some cases (but not always).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Note&lt;/strong&gt;: To develop approximation algorithms we generally use &lt;strong&gt;heuristics&lt;/strong&gt; (defined in the prerequisites).&lt;/li&gt;
&lt;/ul&gt;

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

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

&lt;h2&gt;
  
  
  Optimal solution for the TSP
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;There are many optimal solutions, all of them are too slow since this is an NP-hard problem.&lt;/li&gt;
&lt;li&gt;We call brute force solution a solution that relies on exhaustive search.&lt;/li&gt;
&lt;li&gt;The one I'll show you today is the &lt;strong&gt;Branch-and-Bound&lt;/strong&gt; solution, that consists in backtracking plus pruning unnecessary paths before opening them.&lt;/li&gt;
&lt;li&gt;If you don't understand backtracking check this &lt;a href="https://github.com/godinhojoao/dsa-studies/blob/main/dsa-in-js/backtracking.js" rel="noopener noreferrer"&gt;Github (Code) - Backtracking playground&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Branch-and-Bound for Metric TSP:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Branch&lt;/strong&gt; = check new paths (permutations)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bound&lt;/strong&gt; = check the upper/lower bound and cut if already worse to avoid unnecessary paths

&lt;ul&gt;
&lt;li&gt;lower if is a minimization problem&lt;/li&gt;
&lt;li&gt;upper if is a maximization problem&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;We fix the starting position as 0&lt;/li&gt;
&lt;li&gt;Then we start checking permutations, such as (01230, 02130, 02310, …).&lt;/li&gt;
&lt;li&gt;If we can identify that, before reaching the end, the current path is already worse than our current best, we do not need to continue (prune), and we stop exploring this path.

&lt;ol&gt;
&lt;li&gt;Since TSP is a minimization problem, if our &lt;code&gt;currCost&lt;/code&gt; is already worse than our &lt;code&gt;bestCost&lt;/code&gt; (lower bound), there is no reason to continue exploring that path.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;When we reach the end of a path, we return to start and check if &lt;code&gt;currCost &amp;lt; bestCost&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/godinhojoao/TSP-Approximation-Algorithm/blob/main/src/Graph/Graph.cpp" rel="noopener noreferrer"&gt;Branch-and-Bound code&lt;/a&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Time Complexity: O(V!)&lt;/span&gt;
&lt;span class="c1"&gt;// Space complexity: O(V)&lt;/span&gt;
&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Graph&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;runTSPBranchAndBound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TSPState&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;pathSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;vertices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getVertices&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pathSize&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;vertices&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;lastUsedPos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;back&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;totalCost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currCost&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;getDistance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lastUsedPos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;totalCost&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bestCost&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;totalCost&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bestCost&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;vertices&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visited&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// key diff from brute-force version&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;currDist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getDistance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;back&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;newCost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currCost&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;currDist&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newCost&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bestCost&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// cleaning unnecessary paths (prune = Branch-and-Bound)&lt;/span&gt;
      &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="c1"&gt;// if I use a specific graph where pruning never happens, it becomes as bad as brute force because&lt;/span&gt;
      &lt;span class="c1"&gt;// it ends up exploring the entire search space without cutting any branches&lt;/span&gt;
      &lt;span class="c1"&gt;// fortunately, this is not the case in most situations&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currCost&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;currDist&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visited&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bestCost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bestCost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;runTSPBranchAndBound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop_back&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visited&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currCost&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;currDist&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bestCost&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;But it is still too slow. Since TSP is an NP-hard problem, we still require an approximation algorithm to get a faster, approximate solution for that problem.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Approximation Algorithm for TSP
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The difference between a &lt;strong&gt;heuristic&lt;/strong&gt; and an &lt;strong&gt;approximation algorithm&lt;/strong&gt; is that an approximation algorithm has a proven bound on how far its solution can be from the optimal one.&lt;/li&gt;
&lt;li&gt;For example, a 2-approximation algorithm guarantees that in the worst case it will return a solution at most twice as bad as the optimal one. This is proven mathematically.&lt;/li&gt;
&lt;li&gt;There are multiple approximation algorithms for the same problem, and this also happens in TSP, where you can solve it using, for example, the MST double-tree algorithm, Christofides algorithm, and more.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/godinhojoao/TSP-Approximation-Algorithm/blob/main/src/Graph/Graph.cpp" rel="noopener noreferrer"&gt;The Nearest Insertion Solution (2-approximate algorithm)&lt;/a&gt;
&lt;/h3&gt;

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

&lt;ul&gt;
&lt;li&gt;Image reference: &lt;a href="https://www2.isye.gatech.edu/~mgoetsch/cali/VEHICLE/TSP/TSP009__.HTM" rel="noopener noreferrer"&gt;Nearest Insertion - (Rosenkrantz, Stearns, Lewis, 1974)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/godinhojoao/TSP-Approximation-Algorithm/blob/main/src/Graph/Graph.cpp" rel="noopener noreferrer"&gt;Code here - Graph::runTSPNearestInsertion()&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Time Complexity: O(V^2)&lt;/li&gt;
&lt;li&gt;Space complexity: O(V)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Running Time Comparisons
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Just some examples, on a random computer of ours.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Optimal: time O(V!)
&lt;/h3&gt;

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

&lt;ul&gt;
&lt;li&gt;Branch-And-Bound is already 10x faster than pure brute force for the 11x11 graph. Even though both algorithms have the same worst case, since branch-and-bound prunes some unnecessary paths.&lt;/li&gt;
&lt;li&gt;But as we can see, running it for 15x15 would already take too much time so we didn't.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Approximate: time O(V^2)
&lt;/h3&gt;

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

&lt;ul&gt;
&lt;li&gt;And obviously the approximation is much faster and has a worst-case 2-approximation guarantee, and in our files the worst was not 2 but only ~11.45% worse than the optimal.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Extra: Metaheuristic and Genetic algorithm for TSP
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Metaheuristics in Computer science
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A metaheuristic is a high-level framework with some guidelines to build a heuristic (abstract idea to solve high-level).&lt;/li&gt;
&lt;li&gt;A Genetic heuristic is made of:

&lt;ol&gt;
&lt;li&gt;populations&lt;/li&gt;
&lt;li&gt;select&lt;/li&gt;
&lt;li&gt;crossover&lt;/li&gt;
&lt;li&gt;mutate (small percentage)&lt;/li&gt;
&lt;li&gt;improve&lt;/li&gt;
&lt;li&gt;repeat&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;It still requires problem specific solutions, where we use heuristics, for example:

&lt;ul&gt;
&lt;li&gt;crossover operator (e.g., EAX for TSP)&lt;/li&gt;
&lt;li&gt;local search heuristics (2-opt, k-opt, …)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Genetic Algorithm with Edge Assembly Crossover (EAX-GA)&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It is a TSP heuristic algorithm that does the following:&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Populations&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;N Hamiltonians cycles: [(A→B→C→D→E→A), ham cycle 2, …, ham cycle N]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Select (parents)&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Choose randomly X pairs of hamiltonians cycle within your population&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crossover (Edge Assembly Crossover)&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Combine the pair parent1 and parent2 (both hamiltonian cycles) to generate a child hamiltonian cycle.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mutate&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Here we select a small rate such as 5% of the generated children and we try to improve them by mutating&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local Search&lt;/strong&gt; (apply one heuristic to improve)

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;child = improve_with_2opt_or_LK(child)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repeat N times&lt;/strong&gt; until reaching the stop condition (time or iterations)

&lt;ul&gt;
&lt;li&gt;Now you have a new population of children, you can even get some of the best parents to put in your population and remove the “worst children”.

&lt;ul&gt;
&lt;li&gt;read best as smaller cost and worst as biggest cost to travel&lt;/li&gt;
&lt;li&gt;“Keep best solutions within parents and children and repeat.”&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;And within this population, select, crossover, mutate, and improve with local search once again until reaching the stop condition.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  More solutions for the TSP
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;There are multiple solutions for the TSP:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;2-opt:&lt;/strong&gt; swap two edges (small neighborhood), fast but weak&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;k-opt&lt;/strong&gt;: swap k (constant) edges (larger neighborhood), better solutions, but slower&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lin–Kernighan heuristic (LK):&lt;/strong&gt; adaptive k-opt, usually best trade-off between solution approximation and running time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MST-based&lt;/strong&gt;: build a Minimum Spanning Tree (MST), then do a preorder DFS traversal and shortcut repeated nodes (works for Metric TSP, gives a 2-approximation)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Some of those such as the 2-opt stop in the local minimum, it means that it gets stuck with the best local solutions but it doesn’t mean it’s the global optimal solution.

&lt;ul&gt;
&lt;li&gt;That means: for every pair of edges you try to swap, but the total distance &lt;strong&gt;does not decrease&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;It can’t improve anymore but there may exist better solution.&lt;/li&gt;
&lt;li&gt;The problem isn’t getting stuck in a local minimum, but &lt;strong&gt;how good that local minimum is&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/godinhojoao/TSP-Approximation-Algorithm" rel="noopener noreferrer"&gt;Github (Codes) - TSP-Approximation-Algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Michael Sipser - Introduction to the Theory of Computation, 2nd edition&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ocw.mit.edu/courses/18-404j-theory-of-computation-fall-2020/video_galleries/video-lectures/" rel="noopener noreferrer"&gt;Michael Sipser - Theory of Computation MIT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=zM5MW5NKZJg" rel="noopener noreferrer"&gt;MIT OpenCourseWare - R9. Approximation Algorithms: Traveling Salesman Problem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pages.up.pt/~up233753/ensino/docs/OR/CombinatorialOptimizationHeuristicsLocalSearch.pdf" rel="noopener noreferrer"&gt;Heuristics and Local Search (José Fernando Oliveira, Maria Antónia Carravilla – FEUP)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=1FEP_sNb62k" rel="noopener noreferrer"&gt;Abdul Bari - 7.3 Traveling Salesman Problem – Branch and Bound&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www2.isye.gatech.edu/~mgoetsch/cali/VEHICLE/TSP/TSP009__.HTM" rel="noopener noreferrer"&gt;Nearest Insertion - (Rosenkrantz, Stearns, Lewis, 1974)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>computerscience</category>
      <category>algorithms</category>
      <category>programming</category>
    </item>
    <item>
      <title>Set Data Structure in C</title>
      <dc:creator>João Godinho</dc:creator>
      <pubDate>Fri, 24 Apr 2026 23:11:51 +0000</pubDate>
      <link>https://dev.to/godinhojoao/set-data-structure-in-c-1p4f</link>
      <guid>https://dev.to/godinhojoao/set-data-structure-in-c-1p4f</guid>
      <description>&lt;ul&gt;
&lt;li&gt;In this article I will show how to implement a Set data structure in C using a hashtable, and discuss complexity, trade-offs, and possible improvements.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Basic knowledge of programming (logic, etc);&lt;/li&gt;
&lt;li&gt;C syntax, allocating variables;&lt;/li&gt;
&lt;li&gt;Memory management in C: pointers, malloc, free;&lt;/li&gt;
&lt;li&gt;Basic understanding of hashing;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is a Set?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A data structure that stores &lt;strong&gt;unique elements&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Same idea as in set theory in mathematics: &lt;strong&gt;no repeated values are allowed&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why use a Hashtable?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A hashtable is a good choice for implementing a Set:

&lt;ul&gt;
&lt;li&gt;insert, find, remove -&amp;gt; &lt;strong&gt;O(1) average&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Collisions can happen, so we need a strategy to handle them.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Collision Handling (Chaining)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;In this implementation, collisions are handled using a &lt;strong&gt;linked list per bucket&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Multiple elements that hash to the same index are stored in the same list.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Complexity
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Let:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;m = number of buckets&lt;/li&gt;
&lt;li&gt;k = elements in one bucket&lt;/li&gt;
&lt;li&gt;n = total elements&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Worst case:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;insert O(k)&lt;/li&gt;
&lt;li&gt;find O(k)&lt;/li&gt;
&lt;li&gt;remove O(k)&lt;/li&gt;
&lt;li&gt;iterate O(m + n)&lt;/li&gt;
&lt;li&gt;isEmpty O(1)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Average case:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;insert O(1)&lt;/li&gt;
&lt;li&gt;find O(1)&lt;/li&gt;
&lt;li&gt;remove O(1)&lt;/li&gt;
&lt;li&gt;iterate O(m + n) -&amp;gt; O(n) if m is constant&lt;/li&gt;
&lt;li&gt;isEmpty O(1)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Trade-offs and Improvements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Load factor not handled:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I haven't handled load factor by rehashing since the focus is the Set itself.&lt;/li&gt;
&lt;li&gt;This can degrade performance as &lt;code&gt;n&lt;/code&gt; grows.&lt;/li&gt;
&lt;li&gt;Improvement: dynamic resizing (grow/shrink).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Hashtable vs Balanced Binary Search Tree (BST):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A hashtable is generally better for &lt;strong&gt;unordered sets&lt;/strong&gt;, because it provides constant-time average performance and does not maintain order.&lt;/li&gt;
&lt;li&gt;A balanced BST is preferable only when &lt;strong&gt;worst-case guarantees or sorted order are more important than average performance&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A set is an unordered data structure, so using a hashtable is preferable.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Time complexity comparison (insert, find, remove):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hashtable:&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Average case: &lt;code&gt;O(1)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Worst case: &lt;code&gt;O(n)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Balanced BST:&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Average case: &lt;code&gt;O(log n)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Worst case: &lt;code&gt;O(log n)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;To check load factor handling and hashtable growth using open addressing with double hashing:&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/godinhojoao/dsa-studies/blob/main/dsa-in-c/hashtable.c" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/godinhojoao/dsa-studies/blob/main/dsa-in-c/hashtable.c" rel="noopener noreferrer"&gt;https://github.com/godinhojoao/dsa-studies/blob/main/dsa-in-c/hashtable.c&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Develop a Set in C
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;In this code:

&lt;ul&gt;
&lt;li&gt;A hashtable with fixed size is used&lt;/li&gt;
&lt;li&gt;Collisions are handled with linked lists (chaining)&lt;/li&gt;
&lt;li&gt;FNV-1a is used as the hash function
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdlib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;string.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cp"&gt;#define SET_LIMIT_SIZE 1000
&lt;/span&gt;
&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;SetNode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;SetNode&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;SetNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Set&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;SetNode&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;currLength&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 32-bit FNV-1a&lt;/span&gt;
&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;fnv1a_int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2166136261u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;^=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="mi"&gt;16777619u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;arrLimit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fnv1a_int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;arrLimit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;initializeSet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;currLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SetNode&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;SET_LIMIT_SIZE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;SET_LIMIT_SIZE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;SetNode&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;createNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;SetNode&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;newNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SetNode&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="n"&gt;newNode&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;newNode&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;newNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SET_LIMIT_SIZE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;SetNode&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;currentNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="n"&gt;SetNode&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;prevNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentNode&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentNode&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"set do not allow repeated values: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;prevNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;currentNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentNode&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;SetNode&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;newNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;createNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;currLength&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prevNode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;prevNode&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;currLength&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;SetNode&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SET_LIMIT_SIZE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;SetNode&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;currNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currNode&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;currNode&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;currNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currNode&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;currNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;itemIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SET_LIMIT_SIZE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;SetNode&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;currentNodeToDelete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="n"&gt;SetNode&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;prevNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentNodeToDelete&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;currentNodeToDelete&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;prevNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentNodeToDelete&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;currentNodeToDelete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentNodeToDelete&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentNodeToDelete&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prevNode&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;prevNode&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentNodeToDelete&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentNodeToDelete&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentNodeToDelete&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;currLength&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initializeSet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"== insert 10, 20, 30, 10 ==&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// duplicate test&lt;/span&gt;

  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"== find ==&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"find 10: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"found"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"not found"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"find 99: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"found"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"not found"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"== remove ==&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"remove 99: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"remove 20: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"remove 10: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"remove 10 again: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"== final finds ==&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"find 10: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"found"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"not found"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"find 20: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"found"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"not found"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"find 30: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"found"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"not found"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://web.stanford.edu/class/archive/cs/cs106b/cs106b.1228/lectures/06-set-map/Lecture%206.pdf" rel="noopener noreferrer"&gt;Stanford CS106B Lecture 6: Unordered Data Structures (Sets and Maps)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/godinhojoao/dsa-studies/blob/main/dsa-in-c/set.c" rel="noopener noreferrer"&gt;https://github.com/godinhojoao/dsa-studies/blob/main/dsa-in-c/set.c&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/godinhojoao/dsa-studies/blob/main/dsa-in-c/hashtable.c" rel="noopener noreferrer"&gt;https://github.com/godinhojoao/dsa-studies/blob/main/dsa-in-c/hashtable.c&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>algorithms</category>
      <category>computerscience</category>
      <category>c</category>
    </item>
    <item>
      <title>JVM vs V8: How Java and JavaScript Execution Works</title>
      <dc:creator>João Godinho</dc:creator>
      <pubDate>Sun, 12 Apr 2026 22:55:58 +0000</pubDate>
      <link>https://dev.to/godinhojoao/jvm-vs-v8-how-java-and-javascript-execution-works-5hl0</link>
      <guid>https://dev.to/godinhojoao/jvm-vs-v8-how-java-and-javascript-execution-works-5hl0</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;In this post I’ll discuss the differences between statically and dynamically typed languages, interpreted and compiled languages, what the languages in each set have in common. I’ll also talk about JIT compilation, and in the end compare the V8 engine developed by Google, used in Chrome and Node.js to run JavaScript, and the Java Virtual Machine (JVM).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Concepts before the comparison&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Programming languages classification about typing&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dynamically typed:&lt;/strong&gt; types are determined at runtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Statically typed:&lt;/strong&gt; types are checked at compile time, before execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strongly typed:&lt;/strong&gt; the language strictly enforces type rules and limits implicit type conversions.

&lt;ul&gt;
&lt;li&gt;Strongly typed isn’t a formal term in CS. It is more of a spectrum describing how strict a language is with type rules. Languages with fewer implicit conversions are considered “stronger,” while those with more relaxed conversions are considered “weaker.”&lt;/li&gt;
&lt;li&gt;C is statically typed but generally considered less strictly typed compared to Java due to more permissive casts and lower-level type operations.&lt;/li&gt;
&lt;li&gt;Given this, we could say that Python is stronger than JavaScript when talking about typing, since JavaScript does multiple uncommon type coercions such as &lt;code&gt;1 - "4" = -3&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Programming languages classification about execution model
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compiled language:&lt;/strong&gt; also called &lt;strong&gt;ahead-of-time (AOT) compilation&lt;/strong&gt;, is when the source code is translated into machine code or some lower-level code before execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interpreted language:&lt;/strong&gt; source code is directly executed by another program.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  General characteristics of each language based on their "classification"
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Generally, compiled languages have characteristics such as static typing, strict type checking rules, and fewer type coercions. On the other hand, interpreted languages are generally dynamically typed.&lt;/li&gt;
&lt;li&gt;Furthermore, compiled languages are generally faster than interpreted ones because interpreted languages add runtime overhead by doing type checks (dynamic typing) and other runtime checks that are done ahead of time in compiled languages. There are also other compile-time strategies to achieve performance optimizations.&lt;/li&gt;
&lt;li&gt;There are advantages for both statically/dynamically typed languages:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;compiled and statically typed language&lt;/strong&gt;: robustness because of type checks, performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;interpreted and dynamically typed language&lt;/strong&gt;: productivity because of dynamic types and also portability, since it doesn’t have to be compiled for each architecture, if it contains the interpreter, it will run.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Hybrid model (Just in time compilation - JIT):&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Today it is hard to give a strict binary classification&lt;/strong&gt; for a language’s execution model (compiled or interpreted). We generally have a “hybrid” approach; even languages such as JavaScript that were historically interpreted are not interpreted anymore.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Reference: &lt;a href="https://nodejs.org/learn/getting-started/the-v8-javascript-engine" rel="noopener noreferrer"&gt;NodeJS Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;JIT is a technique to implement the execution model of a language and achieve better runtime speed. Instead of simply compiling or interpreting, with JIT compilation we first interpret the language (JVM bytecode in the case of Java, V8 bytecode in the case of JavaScript) and analyze its initial execution. After that, we can identify hot paths, most-called functions, and the values they are called with. With this information, it compiles these specific parts of the code to machine code to achieve better performance (the JVM does it, V8 does it).&lt;/li&gt;
&lt;li&gt;This is a good example of JIT performance improvement: &lt;a href="https://www.youtube.com/watch?v=d7KHAVaX_Rs" rel="noopener noreferrer"&gt;Just In Time (JIT) Compilers - Computerphile (watch time 5:49)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Virtual Machine
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Virtual machine:&lt;/strong&gt; A software that acts as an emulator of a physical computer system.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;System virtual machines:&lt;/strong&gt; Replicates all that is required to run an etire system, with its own OS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process virtual machine (where JVM is located):&lt;/strong&gt; Also called application virtual machine, because it is used as a single process in the OS that provides a high level abstraction to run compatible languages in any computer architecture since the Process VM abstracts the compilation process to that specific computer architecture. One example is the JVM, in which the developer don’t need to worry about the underlying hardware platform your code is running - providing portability (runs anywhere).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is the Java Virtual Machine (JVM)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The technology responsible for making Java both OS and hardware independent by abstracting specific computer architecture machine code.&lt;/li&gt;
&lt;li&gt;An abstract computing machine, with &lt;a href="https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html" rel="noopener noreferrer"&gt;its own instruction set&lt;/a&gt; (like a real machine that contains move, load, add…). The JVM knows nothing about the Java programming language; actually, it understands only the &lt;code&gt;class file format&lt;/code&gt;, which contains JVM instructions (bytecodes), a symbol table, and more auxiliary information.&lt;/li&gt;
&lt;li&gt;The JVM contains a public spec of its bytecode (the class file format - &lt;code&gt;example.class&lt;/code&gt;), and this JVM bytecode is generated by &lt;code&gt;javac&lt;/code&gt; during the compilation of Java source code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Note:&lt;/strong&gt; Since it contains a public spec of its bytecode and is intended to guarantee long-term support, other languages can also compile to JVM bytecode and enjoy the advantages of not worrying about the underlying computer architecture, having much more portability than with AOT compilation.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Image reference: &lt;a href="https://upload.wikimedia.org/wikipedia/commons/d/dd/JvmSpec7.png" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is V8 JavaScript Engine
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An engine that executes ECMAScript and WebAssembly specifications and handles memory allocation, garbage collection, and more. It contains AST parsing logic, an interpreter (Ignition), an internal bytecode, JIT compilers, and more specific logic to run the source code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why is V8 an engine and not a process VM like the JVM?&lt;/strong&gt; The reason is that, differently from the JVM, which contains a public spec for the bytecode it runs (generated by &lt;code&gt;javac&lt;/code&gt; and other compilers), V8 takes source JavaScript code (not lower-level code like bytecode) and uses Ignition to generate its bytecode. It doesn’t contain a public spec of its bytecode and doesn’t guarantee long-term support for it. Although V8 works similarly to the JVM to execute code, it contains important differences.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JVM:&lt;/strong&gt; abstract definition of a machine that contains its bytecode spec, instruction set, and more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;V8:&lt;/strong&gt; real implementation of the execution model of ECMAScript and WebAssembly specs.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Simplified execution model
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Javascript with V8
&lt;/h3&gt;

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

&lt;ul&gt;
&lt;li&gt;image reference: &lt;a href="https://medium.com/dailyjs/understanding-v8s-bytecode-317d46c94775" rel="noopener noreferrer"&gt;Franziska Hinkelmann&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;JS source code -&amp;gt; AST -&amp;gt; Ignition (interpreter) compiles to bytecode -&amp;gt; Ignition  executes its bytecode -&amp;gt; JIT hot paths with Turbofan or Crankshaft&lt;br&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx1isb8y9kbt1esa78ro4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx1isb8y9kbt1esa78ro4.png" alt="v8 executing js diagram2" width="602" height="249"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;image reference: &lt;a href="https://v8.dev/blog/ignition-interpreter" rel="noopener noreferrer"&gt;V8 documentation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Java with JVM
&lt;/h3&gt;

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

&lt;h2&gt;
  
  
  So why people say JS is interpreted and Java is compiled if in practice both use JIT?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Although we saw that languages actually use a hybrid approach, we may ask ourselves: “Why do people call these languages interpreted or compiled if in reality it is not even true anymore?”

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Because they actually have strong reasons to do it.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  1st: Traditional definitions vs real execution model
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Code runs without an explicit compilation step = interpreted.&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;In this case, &lt;strong&gt;JS source code is executed by V8&lt;/strong&gt;, which first compiles the code into Ignition bytecode, then runs it through an interpreter, and uses JIT to compile JS hot paths (most used code) into machine code and execute them (hybrid approach). Even though it is a hybrid approach, the fact that execution starts immediately in a runtime is why we put it in the &lt;strong&gt;“interpreted languages bag.”&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Code is translated to a lower level before execution = compiled.&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Java is compiled to bytecode&lt;/strong&gt; using &lt;code&gt;javac&lt;/code&gt;, and then the bytecode is executed in the JVM, which uses an interpreter and also JIT to compile hot paths into native machine code. It is also a hybrid approach because it runs bytecode and compiles hot paths to native machine code, but since there is &lt;strong&gt;a clear step where the source code is compiled into bytecode before execution&lt;/strong&gt;, we put it in the &lt;strong&gt;“compiled languages bag.”&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  2nd: Historical design of these languages (Java and JS)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Because they were historically created like this, with these “style decisions” (compiled or interpreted) reflected in their syntax and way of working.

&lt;ul&gt;
&lt;li&gt;For example, &lt;strong&gt;Java&lt;/strong&gt; was designed as a statically and strongly typed language with a clear compilation step, focused on large-scale systems where correctness and structure before execution are important.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript&lt;/strong&gt;, on the other hand, was created as a lightweight scripting language for the browser. It is dynamically typed, making it more flexible and permissive, which was ideal for quick scripting and web interactivity, even if it can introduce more runtime ambiguity and errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript&lt;/strong&gt; is extremely permissive, allowing users to do unusual things to avoid breaking browser web pages (type coercion is a good example).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Which nomenclature to use for JS and Java and other languages (interpreted or compiled)?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Based on what I pointed out, the traditional nomenclatures (JS interpreted and Java compiled) are good choices.&lt;/li&gt;
&lt;li&gt;But it’s important to understand how their execution model really works to achieve modern day performance, and also that we don’t stand still when it comes to programming languages’ execution models.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Note:&lt;/strong&gt; Obviously, there are “pure” compilers or interpreters without this hybrid approach. &lt;a href="https://gcc.gnu.org/" rel="noopener noreferrer"&gt;&lt;strong&gt;GNU Compiler Collection -&lt;/strong&gt; GCC&lt;/a&gt;, for example, is an ahead-of-time (AOT) compiler for C and other languages.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/javase/specs/jvms/se26/html/jvms-1.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/javase/specs/jvms/se26/html/jvms-1.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/cd/E57471_01/bigData.100/extensions_bdd/src/cext_transform_typing.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/cd/E57471_01/bigData.100/extensions_bdd/src/cext_transform_typing.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://v8.dev/docs" rel="noopener noreferrer"&gt;https://v8.dev/docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/learn/getting-started/the-v8-javascript-engine" rel="noopener noreferrer"&gt;https://nodejs.org/learn/getting-started/the-v8-javascript-engine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=d7KHAVaX_Rs" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=d7KHAVaX_Rs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/dailyjs/understanding-v8s-bytecode-317d46c94775" rel="noopener noreferrer"&gt;https://medium.com/dailyjs/understanding-v8s-bytecode-317d46c94775&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=p-iiEDtpy6I" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=p-iiEDtpy6I&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Virtual_machine" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/Virtual_machine&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>computerscience</category>
      <category>java</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>Consistency Models Part 1 - Linearizability</title>
      <dc:creator>João Godinho</dc:creator>
      <pubDate>Mon, 06 Apr 2026 15:12:21 +0000</pubDate>
      <link>https://dev.to/godinhojoao/consistency-models-part-1-linearizability-1bap</link>
      <guid>https://dev.to/godinhojoao/consistency-models-part-1-linearizability-1bap</guid>
      <description>&lt;h2&gt;
  
  
  Distributed Systems
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A system composed of multiple components running on different computers that communicate over a network to work together coherently.&lt;/li&gt;
&lt;li&gt;“A distributed system is a collection of independent computers that appears to its users as a single coherent system.” - Tanenbaum and van Steen&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Consistency in Distributed Systems
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Consistency is a broader term that means multiple things depending on context.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;In database context (ACID):&lt;/strong&gt; consistency means the system moves from one valid state to another, preserving all constraints and invariants (e.g. unique fields, foreign keys, business rules).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read-after-write consistency&lt;/strong&gt;: what value you see when you read data after writes happen across multiple nodes, read last written data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replication:&lt;/strong&gt; Replica should be in the same state as other replicas (when? it can vary depending on your context).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;As shown there are multiple definitions for consistency, and many to choose from.&lt;/li&gt;
&lt;li&gt;You can achieve different consistency levels in your application.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Consistency Models
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Consistency models are patterns regarding the different levels of consistency we can achieve in different aspects of a distributed system. For example, replication consistency, read-after-write consistency, and many others. People often informally call them “weak consistency” and “strong consistency”, but there are shades of consistency between these.&lt;/li&gt;
&lt;li&gt;When talking about consistency models and focusing on data replication and read-after-write aspects, consistency is generally observed through reads, but it depends on how writes are handled.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;For read operations:&lt;/strong&gt; the guarantees about which value is returned by a read in the presence of replication and concurrent writes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For write operations:&lt;/strong&gt; the guarantees about how writes are ordered and propagated across replicas.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Each consistency model has trade-offs, and understanding them is crucial for selecting the best option for your business context. Today I'll discuss what is called “strong consistency”. I will not refer to it as &lt;strong&gt;“strong consistency”&lt;/strong&gt; because this isn't a formal term. The formal one is &lt;strong&gt;linearizability&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example where Consistency is a Point of Attention
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Imagine a scenario where you have an e-commerce system built as a distributed system, similar to Amazon, containing multiple services.&lt;/li&gt;
&lt;li&gt;Look at the diagram below to see these services:&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Customer A accesses your e-commerce system and buys a product. Then OrderService creates an order, PaymentService processes the payment, and only then StockService updates the stock. But imagine that during the payment process, Customer B also buys the same product, and there are not enough units of this product in stock.&lt;/li&gt;
&lt;li&gt;This is a scenario in which consistency becomes a problem that requires an architectural decision using the most appropriate consistency model for your context.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Business-Driven Consistency Trade-offs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Given the example above, you could imagine different requirements:&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Prevent oversell:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;“The better option is to avoid selling products we don’t have.” For this, you would prefer not to allow a purchase if the product isn’t available in stock.

&lt;ul&gt;
&lt;li&gt;You avoid inconsistency by enforcing constraints at write time, typically using atomic operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic:&lt;/strong&gt; the operation happens entirely or nothing changes. If it fails, no state is modified.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Allow oversell:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;“It is acceptable to let multiple users buy because I can figure out how to restock later, but I cannot lose a sale.” For that, you would allow purchases even if stock is not available.

&lt;ul&gt;
&lt;li&gt;You would then need to handle it with business options such as refunds or waiting for stock.&lt;/li&gt;
&lt;li&gt;As you can see, these are different consistency trade-offs. One is more strict, and the other is more relaxed. We cannot say one is better than the other, it depends on the context and business rules.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Trade-off: Availability vs Consistency
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Sometimes you will want to favor high availability over consistency, but there are cases where linearizability is crucial. There is no fixed rule, you need to identify the specific parts of your system and their functional requirements (what the system must do) as well as their non-functional requirements (how the system should behave, such as performance, availability, and consistency constraints).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Linearizability - Multiple replicas behave as a single system&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;We have multiple servers and database replicas, but all operations behave as if they were executed on a single database. Every operation takes effect atomically from the user's perspective, not necessarily in the database internals. The focus is on client-observable behavior: when and what the operations return. We ignore the internal replication mechanism.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic operations in distributed systems:&lt;/strong&gt;

&lt;ol&gt;
&lt;li&gt;Either all nodes commit, or all abort.&lt;/li&gt;
&lt;li&gt;If any node crashes, the system ensures the operation is aborted or completed safely.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;The concept of linearizability is not restricted to distributed systems. It is also used in concurrent programming in general with shared memory.&lt;/li&gt;
&lt;li&gt;Up-to-date value is returned for subsequent reads after one write. “Up-to-date” means that &lt;strong&gt;if write A finishes before read B starts&lt;/strong&gt;, there is a &lt;strong&gt;real-time dependency&lt;/strong&gt; to respect.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Image reference: &lt;a href="https://www.youtube.com/watch?v=noUNH3jDLC0" rel="noopener noreferrer"&gt;&lt;strong&gt;Martin Kleppmann&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Linearizability != Serializability&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Linearizability:&lt;/strong&gt; operations appear to execute in real time order. If A finishes before B starts, then B must see the effect of A.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serializability:&lt;/strong&gt; transactions produce a result equivalent to some serial (one-by-one) execution order. The order does not have to follow real time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Examples of Linearizability with Real-time Dependency
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Case1:&lt;/strong&gt; A writes x = 1 → A finishes → B reads x = 1 &lt;strong&gt;(even for different DB replicas)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Case 2:&lt;/strong&gt; (this is subject of common confusion - overlapping operations)

&lt;ul&gt;
&lt;li&gt;A starts x = 1&lt;/li&gt;
&lt;li&gt;B starts reading x (while A is still in progress) → returns x = 0&lt;/li&gt;
&lt;li&gt;A finishes x = 1&lt;/li&gt;
&lt;li&gt;This is &lt;strong&gt;linearizable and valid&lt;/strong&gt;, because the read overlaps the write, so the system can order it as: B → A&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Case 3&lt;/strong&gt;: (overlapping operations the other way is &lt;strong&gt;also valid&lt;/strong&gt;)

&lt;ul&gt;
&lt;li&gt;A starts x = 1&lt;/li&gt;
&lt;li&gt;B starts reading x (while A is still in progress)&lt;/li&gt;
&lt;li&gt;A finishes x = 1&lt;/li&gt;
&lt;li&gt;B returns x = 1&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Ways of Achieving Linearizability
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Typically requires &lt;strong&gt;synchronous data replication&lt;/strong&gt; across multiple servers OR &lt;strong&gt;coordination mechanisms (such as quorum)&lt;/strong&gt; to ensure necessary replicas reflect the latest write before reads are served.&lt;/li&gt;
&lt;li&gt;Below there is a sync replication example, but it could be async using quorum or leader consensus.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;And below there is a linearizability issue in which the write did not respect quorum, so when reading, client 3 got an invalid value (v0). The write happened from client 1 only to DB node A, so when reading from a quorum of 2 nodes does not work correctly, because B and C are not up to date with real-time dependencies, which are required for a linearizable system.&lt;/li&gt;
&lt;/ul&gt;

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

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

&lt;ul&gt;
&lt;li&gt;Image references: &lt;a href="https://www.youtube.com/watch?v=noUNH3jDLC0" rel="noopener noreferrer"&gt;&lt;strong&gt;Martin Kleppmann&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;To solve the issue above following the quorum pattern, some solutions:

&lt;ul&gt;
&lt;li&gt;Solution 1 - &lt;strong&gt;write quorum:&lt;/strong&gt; set applied to a minimum quorum of N nodes - in this case 2 of 3.&lt;/li&gt;
&lt;li&gt;Solution 2 - &lt;strong&gt;read repair&lt;/strong&gt;: still allow set only on one DB node, but once the server (client requesting data) identifies in a get request that a node has stale data, it will resend a set with the newest data to all nodes containing old data and will wait for at least one DB node to acknowledge.

&lt;ul&gt;
&lt;li&gt;In our case, client 2 on the get request would identify that node A is up to date and node B is outdated, then would send a set request to both B and C and wait for at least one to respond.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;We will discuss more about practical implementations of consistency models such as quorum in further articles.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use cases of Linearizability:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Systems that require strong consistency guarantees where operations must reflect the most recent completed write. Examples: banking/financial systems (you don’t want stale balances when updating accounts), flight booking systems (you don’t want multiple people successfully booking the same seat).

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;no stale reads after a completed write&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;strong ordering of operations&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;correctness under concurrency&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Linearizability Pros and Cons:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trade-off:&lt;/strong&gt; serving up-to-date data, but with higher latency and coordination cost. This involves a trade-off between consistency and availability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pros:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No stale data for completed writes&lt;/strong&gt;, reads always see the latest completed value.&lt;/li&gt;
&lt;li&gt;Simpler application logic in some cases because there is no need to handle read conflicts or inconsistent states.&lt;/li&gt;
&lt;li&gt;Guarantees correctness in concurrent systems even when many operations happen at the same time.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cons:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Higher latency&lt;/strong&gt;, since writes (and sometimes reads) require coordination across replicas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lower throughput&lt;/strong&gt;, because coordination limits how many operations can be processed in parallel. Higher latency also contributes to reduced throughput.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced availability&lt;/strong&gt;, because operations may block or fail if enough replicas are not reachable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More complex infrastructure&lt;/strong&gt;, since it requires coordination protocols (e.g., leader election, quorum, consensus-based replication).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Linearizability Common Mistakes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A strongly consistent storage layer can still be part of a system that serves stale data if caches are involved. In that case, the database state is correct, but the overall system may return stale cached values, so it is &lt;strong&gt;no longer linearizable from the client’s perspective.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;When a system implements linearizability, the cache must not break the guarantee that reads return the most recent committed write.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Exemplifying:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Think of a CDN or browser cache. With TTL, users may see stale data even if the database has the latest value. This is a freshness issue, not a consistency issue.&lt;/li&gt;
&lt;li&gt;In distributed systems, even with linearizability, stale reads can still happen outside the database layer due to caches or read paths that bypass the primary source of truth.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;In this case:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Database:&lt;/strong&gt; still linearizable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entire system (DB + cache + app):&lt;/strong&gt; NOT linearizable&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If you don't know cache basics, check my last article: &lt;a href="https://dev.to/godinhojoao/cache-layers-in-modern-applications-563e"&gt;https://dev.to/godinhojoao/cache-layers-in-modern-applications-563e&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final thoughts:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trade-off between availability and consistency is important&lt;/strong&gt; depending on your context. Since we can’t guarantee both, if some nodes are down, a linearizability model may become unavailable, but eventual consistency can still work even with some nodes down.&lt;/li&gt;
&lt;li&gt;It is easier to ensure linearizability when your application doesn't handle multiple concurrent writes.&lt;/li&gt;
&lt;li&gt;And probably you've noticed that:

&lt;ul&gt;
&lt;li&gt;“As the system grows, despite the technology, you can’t rely on consistency.” - &lt;a href="https://learn.microsoft.com/en-us/archive/msdn-magazine/2014/august/cutting-edge-documents-databases-and-eventual-consistency" rel="noopener noreferrer"&gt;Dino Esposito&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;You can build linearizable systems at large scale, but this highlights the cost in latency, coordination, and complexity, not to mention availability trade-offs. If it is required in your case, it is still the right choice.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Want to understand more about consistency models? I’ll continue developing content in this series about consistency models and later cover practical implementations of each model.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.cl.cam.ac.uk/teaching/2122/ConcDisSys/dist-sys-notes.pdf" rel="noopener noreferrer"&gt;Concurrent and Distributed Systems - MIT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=noUNH3jDLC0&amp;amp;list=PLeKd45zvjcDFUEv_ohr_HdUFe97RItdiB&amp;amp;index=20" rel="noopener noreferrer"&gt;Distributed Systems 7.2: Linearizability - Martin Kleppmann&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pdos.csail.mit.edu/6.824/notes/l-linearizability.txt" rel="noopener noreferrer"&gt;https://pdos.csail.mit.edu/6.824/notes/l-linearizability.txt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pdos.csail.mit.edu/6.824/papers/linearizability-faq.txt" rel="noopener noreferrer"&gt;https://pdos.csail.mit.edu/6.824/papers/linearizability-faq.txt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jepsen.io/consistency/models" rel="noopener noreferrer"&gt;https://jepsen.io/consistency/models&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>distributedsystems</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Cache Layers in Modern Applications</title>
      <dc:creator>João Godinho</dc:creator>
      <pubDate>Sun, 29 Mar 2026 21:17:42 +0000</pubDate>
      <link>https://dev.to/godinhojoao/cache-layers-in-modern-applications-563e</link>
      <guid>https://dev.to/godinhojoao/cache-layers-in-modern-applications-563e</guid>
      <description>&lt;h2&gt;
  
  
  What is Cache?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Any temporary storage for copies of data in order to get faster responses. It can be related to hardware, where the CPU accesses caches (L1, L2, L3) to store copies of data from main memory and improve access speed. This happens due to multiple factors: the technology used to build caches (L1, L2, L3) is different from the one used for main memory, making it faster but more expensive, and it is also physically closer to the CPU, which reduces data transfer latency. The cache typically stores recently and/or frequently accessed data.

&lt;ul&gt;
&lt;li&gt;Note: Cache is different from a &lt;a href="https://dev.to/godinhojoao/data-buffer-3f25"&gt;Data Buffer&lt;/a&gt;, since a buffer is used as temporary storage not to get the most frequently and recently used data, but to manage the problem when we have consumers and producers operating at different rates.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;In this post we will discuss primarily cache as internet technologies used to provide scalability, performance, cost reduction and more (focusing on system design). For example, web browsers cache HTML, images, and more after first load; cache of DNS records on the OS; CDN servers cache content to reduce latency. (We will not discuss hardware caches, DNS cache, DB caches and other caches, but they are also important)&lt;/li&gt;

&lt;li&gt;Covered topics: the role of cache, cache layers, and how to combine them to achieve better performance and scalability.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why to use caching?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Increase performance:&lt;/strong&gt; Reading from memory is much faster than from disk, resulting in faster data access. Itsignificantly reduces database I/O and increases read throughput.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduce costs:&lt;/strong&gt; Reduces database costs since cache can reduce database load, allowing you to reduce the number of DB instances, and if the DB service or VPS charges per throughput caching will also reduce costs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability:&lt;/strong&gt; With in-memory cache we can handle application access spikes more easily, such as Black Friday. Caching most accessed data is crucial and ensures the app will handle the load without DB bottlenecks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;There are two main aspects about caching:&lt;/strong&gt; performance and data freshness.

&lt;ul&gt;
&lt;li&gt;We want to serve the fastest responses possible while still providing the required data freshness. This can vary depending on the application domain, for example, the price of a product should never be stale, but a blog post in general doesn’t require freshness.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  CACHE HIT and CACHE MISS
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;HIT = data is in cache, use it from cache (faster load)&lt;/li&gt;
&lt;li&gt;MISS = data isn’t in cache, save it to cache&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Considerations before Cache layers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This nomenclature and numbering are not formal definitions. I am using them as a way to explain and make caching layers easier to understand. Don’t confuse this with memory hierarchy or OSI layers; the way I used this has nothing to do with either. It is &lt;strong&gt;only a naming approach to help understand different levels of caching.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Layer 1: Browser Cache (plus Concept of Time to Live)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A natural starting point is web browsers, one of the most used categories of applications in the contemporary world. As the name suggests, this cache isn’t handled by web developers but by the browser application itself, although developers can configure it correctly in their server responses, we will cover this topic below, first let’s understand this mechanism.&lt;/li&gt;
&lt;li&gt;When a user first accesses a web page, the browser loads a huge amount of data, and to avoid loading it after every refresh it stores it on the user’s disk with a &lt;strong&gt;Time to Live - which determines how long the copy will stay stored in the cache -&lt;/strong&gt; this copy can be deleted in some scenarios: time to live expires, using ETags, or the cache is full and needs to be replaced with other data.&lt;/li&gt;
&lt;li&gt;As mentioned, it is provided by browsers, and is the best cache we can have since it is on the user’s computer, avoiding hitting our CDN servers or origin servers. But the developer needs to analyze it carefully since if you use it incorrectly you will face users with an outdated cache that you can’t clean programmatically because of misconfiguration.&lt;/li&gt;
&lt;li&gt;It also works similarly on mobile applications, utilizing HTTP headers to configure cache constraints.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  HTTP Cache Headers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It’s crucial to configure the cache correctly by setting the best matching HTTP cache headers for server responses.&lt;/li&gt;
&lt;li&gt;With HTTP cache headers you can configure not only browser caches but other caches that we will discuss throughout this article. A key point is to understand that you can tell the cache layer (in this case the web browser) things like:

&lt;ul&gt;
&lt;li&gt;1 “DON’T CACHE THIS”&lt;/li&gt;
&lt;li&gt;2 “CACHE FOR THIS TIME X”&lt;/li&gt;
&lt;li&gt;3 “CACHE USING THIS TAG X, AND I TELL YOU IF SOMETHING CHANGED”

&lt;ul&gt;
&lt;li&gt;This last approach allows developers to update cache programmatically without the need for a TTL or the user manually cleaning the cache.&lt;/li&gt;
&lt;li&gt;The browser sends a request with the ETag header before using data from cache, and the server can respond with one of the following: 1st - “304 Not Modified”, which means “use from cache” or 2nd - “200 OK”, which means “new response not from cache, update cache”&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Obviously there are multiple concerns about HTTP cache headers that I will not discuss here. For more about HTTP cache headers &lt;a href="https://dev.to/godinhojoao/-http-caching-101-4ib0"&gt;HTTP Caching 101&lt;/a&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Layer 2: Content Delivery Network Cache
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://dev.to/godinhojoao/how-content-delivery-networks-work-2epj"&gt;CDN&lt;/a&gt; is a reverse proxy server that lives closer to end users, and can be used for many server optimizations, one of which is caching.&lt;/li&gt;
&lt;li&gt;Everything that we’ve discussed about HTTP cache headers for browsers works &lt;strong&gt;almost the same&lt;/strong&gt; here with an important difference: it works like a “&lt;strong&gt;centralized cache&lt;/strong&gt;”, not on the user’s machine.&lt;/li&gt;
&lt;li&gt;When talking about browser cache, you’ve ignored the following situation:

&lt;ul&gt;
&lt;li&gt;Imagine that you have an admin user that accesses the &lt;code&gt;/admin&lt;/code&gt; route, should you cache the response data for all your users? NO.&lt;/li&gt;
&lt;li&gt;If you still want to cache it, you need to better understand HTTP cache headers and set &lt;code&gt;Cache-Control: private&lt;/code&gt;, which tells the CDN to not store it, only the web browser.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Layer 3: Reverse Proxy Cache (Infrastructure)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;In a case where you have multiple server instances running in a single region, you can use a reverse proxy in front of these instances to load balance traffic among them and also cache their responses. It is also configured using HTTP headers.&lt;/li&gt;
&lt;li&gt;It can be implemented using Varnish, Nginx, or other reverse proxies.&lt;/li&gt;
&lt;li&gt;To understand better, read the section about reverse proxy in: &lt;a href="https://dev.to/godinhojoao/how-content-delivery-networks-work-2epj"&gt;How Content Delivery Networks&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Layer 4: Application Cache
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;There are two scopes for this application cache: &lt;strong&gt;Local and Distributed&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local application cache:&lt;/strong&gt; Only one instance has access to it (in-process cache)

&lt;ul&gt;
&lt;li&gt;In the case where you have multiple instances, using local cache would introduce consistency issues, since each instance has its own local memory that is not shared.&lt;/li&gt;
&lt;li&gt;If you have a single instance, it’s clearly a good idea to use a local in-memory application cache, since it’s the fastest type of cache available.&lt;/li&gt;
&lt;li&gt;This reminds us of the idea of memoization, which is storing function results in memory for future use to improve computation speed. But here it is used for frequently accessed data.&lt;/li&gt;
&lt;li&gt;Ensure you manage it correctly to avoid excessive memory usage; for that, use libraries in your language, for example: &lt;a href="https://www.npmjs.com/package/lru-cache" rel="noopener noreferrer"&gt;NPM lru-cache - JS&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Distributed application cache:&lt;/strong&gt; Used when you have multiple instances (Redis/Memcached)

&lt;ul&gt;
&lt;li&gt;As mentioned, horizontal scaling requires more than a single instance server, and for that we need a shared cache, also called a distributed cache.&lt;/li&gt;
&lt;li&gt;It is slower than local in-memory cache since distributed cache adds network communication overhead. However, it is still much faster than querying the database directly.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Could we mix both local and distributed?&lt;/strong&gt; Yes, by trying to achieve something similar to the hardware memory hierarchy, where we have different cache levels. However, it is important to take care with this approach since it can lead to consistency issues. If serving stale data for a short window is acceptable, this approach can work; otherwise, you should avoid mixing both strategies.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Last observations about these Cache Layers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;People tend to think that only application cache exists, and also think that they can only use Redis/Memcached as distributed caches. This is an enormous mistake that can lead to low performance and high costs. Imagine having a multi-region application and a single Redis instance placed in one region, then you would pay too much latency for all other regions that do not share the same region as your Redis/Memcached.&lt;/li&gt;
&lt;li&gt;Another thing is: not only static assets can be cached on browser and CDNs, you can and may cache your API responses.&lt;/li&gt;
&lt;li&gt;The combination of these layers is the best case for application performance and scalability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start with the simplest caching layer that solves your problem. Browser caching and CDN caching are almost free. Application-level caching is the next step. Only add more complexity (write-through, stampede prevention, stale-while-revalidate) when you have evidence that you need it.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Caching caveats (mainly cache invalidation)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;There are only two hard things in Computer Science: cache invalidation and naming things.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;— Phil Karlton&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Caching isn’t all about lightning speed, but also about using the best caching strategy to avoid serving stale data and crashing servers, especially at the application-level cache.

&lt;ul&gt;
&lt;li&gt;If you don’t use the correct cache strategy, you will probably find yourself using too many resources to achieve what someone who knows cache strategies and when to use each can achieve. This is not the main topic of this article, but a discussion for a future one.&lt;/li&gt;
&lt;li&gt;You can serve stale data by not updating it in cache when it is necessary.&lt;/li&gt;
&lt;li&gt;You can crash your server in several ways with misuse of caching:

&lt;ul&gt;
&lt;li&gt;A hot key suddenly becomes invalid and then multiple DB calls happen at the same time to revalidate cache.&lt;/li&gt;
&lt;li&gt;If you are using a local cache solution in your origin server that keeps growing indefinitely and consumes all your RAM.&lt;/li&gt;
&lt;li&gt;Distributed cache failure: your entire cache cluster goes down and now all traffic falls back to the DB.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;One example of misuse of caching is: using a high-cardinality cache key that is never reused: &lt;code&gt;user:{timestamp}&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Congrats! By doing this you now have only unnecessary extra trips that always result in a miss: &lt;code&gt;cache read -&amp;gt; miss -&amp;gt; db -&amp;gt; cache save&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;There are multiple things to take care of when using caching and multiple strategies to consider, but it can be a game changer for your application if well set. Stale data issues tend to be the most frequent ones. Also, never forget race conditions and other common issues that will be discussed in a future post.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.cloudflare.com/learning/cdn/what-is-caching/" rel="noopener noreferrer"&gt;https://www.cloudflare.com/learning/cdn/what-is-caching/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/caching/" rel="noopener noreferrer"&gt;https://aws.amazon.com/caching/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.carbonatethis.com/articles/2025-09-26-web-caching-at-scale" rel="noopener noreferrer"&gt;https://www.carbonatethis.com/articles/2025-09-26-web-caching-at-scale&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.akamai.com/glossary/what-is-dns-caching" rel="noopener noreferrer"&gt;https://www.akamai.com/glossary/what-is-dns-caching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/pdfs/whitepapers/latest/database-caching-strategies-using-redis/database-caching-strategies-using-redis.pdf" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/pdfs/whitepapers/latest/database-caching-strategies-using-redis/database-caching-strategies-using-redis.pdf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kyleshevlin.com/memoization/" rel="noopener noreferrer"&gt;https://kyleshevlin.com/memoization/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://martinfowler.com/bliki/TwoHardThings.html" rel="noopener noreferrer"&gt;https://martinfowler.com/bliki/TwoHardThings.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>distributedsystems</category>
      <category>computerscience</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>How Content Delivery Networks Work</title>
      <dc:creator>João Godinho</dc:creator>
      <pubDate>Wed, 25 Mar 2026 03:15:41 +0000</pubDate>
      <link>https://dev.to/godinhojoao/how-content-delivery-networks-work-2epj</link>
      <guid>https://dev.to/godinhojoao/how-content-delivery-networks-work-2epj</guid>
      <description>&lt;h2&gt;
  
  
  Introduction - Content Delivery Network
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;CDNs are important because they improve performance, scalability, availability, and reliability of services. Understanding what they are can help you not only work with them but also apply their ideas to solve similar problems in your own reverse proxies or infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Short history:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Before CDNs, websites depended on single origin servers, causing high latency and outages under traffic spikes. In the late 1990s, Akamai introduced distributed caching and routing users to nearby servers, selling it as a premium acceleration service for large enterprises. With the rise of video platforms like Youtube and Netflix, CDNs became essential to handle massive global traffic and streaming. Over time, pricing dropped and delivery became commoditized, so today CDNs compete mainly through services on top, such as WAF, DDoS protection, TLS termination, bot management, API protection, analytics, and edge compute, not just content delivery.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Forward and Reverse Proxy
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Since a CDN is a reverse proxy, we need to first understand what a reverse proxy is.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Forward Proxy
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Normally called just “Proxy”, it is a server placed in front of client machines that intercepts their internet requests and communicates with external servers on their behalf, acting as a middleman.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcf-assets.www.cloudflare.com%2Fslt3lc6tev37%2F2MZmHGnCdYbQBIsZ4V11C6%2F25b48def8b56b63f7527d6ad65829676%2Fforward_proxy_flow.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcf-assets.www.cloudflare.com%2Fslt3lc6tev37%2F2MZmHGnCdYbQBIsZ4V11C6%2F25b48def8b56b63f7527d6ad65829676%2Fforward_proxy_flow.png" alt="forward proxy" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Image reference - &lt;a href="https://www.cloudflare.com/learning/cdn/glossary/reverse-proxy/" rel="noopener noreferrer"&gt;Cloudflare Learning&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What is a Forward Proxy used for?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;To protect the user's identity online:&lt;/strong&gt; Only the IP of the proxy will be easily known, but the real IP of the user may be harder to identify. (e.g. avoid censorship from a tyrant government)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;To bypass firewall restrictions:&lt;/strong&gt; For example, when a college firewall blocks specific websites, a user can still access the proxy, the proxy accesses the website A, and forwards responses to the user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;To block access to certain content:&lt;/strong&gt; Also used for the opposite as seen before, for example, a school network configured to connect to the web through a forward proxy can refuse to forward responses from specific sites.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Reverse Proxy
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Instead of being a man in the middle between client and server, a reverse proxy acts as a man in the middle between a client and one or more servers. The client requests one server, but it first reaches a reverse proxy that forwards the request to one or more servers (to the real origin server).&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Image reference - &lt;a href="https://www.cloudflare.com/learning/cdn/glossary/reverse-proxy/" rel="noopener noreferrer"&gt;Cloudflare Learning&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What is a Reverse Proxy used for?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Load balancing: For some cases we can't handle all the requests with only one origin server, and for that we use multiple origin servers in which we need to balance traffic across them. For that we use a reverse proxy as a load balancer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protection from attacks&lt;/strong&gt;: With a reverse proxy the origin server doesn't need to reveal its IP, making attacks harder to make. And also implementing good security techniques in this reverse proxy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching:&lt;/strong&gt; There are cases where you have multiple services around the globe, for example, a Brazil Server and an England Server, but you have Reverse Proxies that forward traffic to the closest server. However, to reach the origin server every time you lose a lot of time, and for that you can cache responses in the reverse proxy closest to the user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSL encryption:&lt;/strong&gt; Instead of doing SSL or TLS encryption and decryption in all your origin servers, you can do it with a single reverse proxy, freeing resources from your real origin servers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  TLDR: Forward Proxy vs Reverse Proxy
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The difference is subtle and important. It is not only about where the proxy sits, but also about its purpose.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Forward Proxy:&lt;/strong&gt; Sits in front of the client and prevents the origin server from communicating directly with the user. Client → Forward Proxy → Internet → Origin Server

&lt;ul&gt;
&lt;li&gt;In front of client; Forward user requests to the internet&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Reverse Proxy:&lt;/strong&gt; Sits in front of the origin server and prevents the user from communicating directly with the origin server. Client → Internet → Reverse Proxy → Origin Server

&lt;ul&gt;
&lt;li&gt;In front of our servers; Manage incoming traffic from the internet to our servers.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to implement a Reverse Proxy?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You can use existent services such as NGINX, APACHE, and more.&lt;/li&gt;
&lt;li&gt;You can build your own (really hard and probably you will not achieve same results).&lt;/li&gt;
&lt;li&gt;You can use a CDN, but sometimes you will use both: a CDN for edge caching and a reverse proxy near the origin servers for caching, routing, security and load balancing.

&lt;ul&gt;
&lt;li&gt;This is useful in scenarios where you want to take advantage of both strategies.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  FINALLY: What is a CDN in practice?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A reverse proxy… Eh, actually, multiple reverse proxies spread across the globe.&lt;/li&gt;
&lt;li&gt;Maybe you were expecting something bigger since CDNs solve huge problems, but that's it. The power of a CDN resides, obviously, in the software, but mainly in the infrastructure the company has: with redundant energy, multiple data center sites, and reverse proxies with superpowers such as caching, routing, load balancing, and edge networks.&lt;/li&gt;
&lt;li&gt;“ A content delivery network (CDN) is a geographically distributed group of servers that caches content close to end users. A CDN allows for the quick transfer of assets needed for loading Internet content, including HTML pages, JavaScript files, stylesheets, images, and videos.” - &lt;a href="https://www.cloudflare.com/learning/cdn/what-is-a-cdn/" rel="noopener noreferrer"&gt;Cloudflare Learning&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How does a CDN work?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;As mentioned, the main goal of a CDN is to deliver content faster, cheaper, and more reliably.&lt;/li&gt;
&lt;li&gt;For this to work, a CDN is a network of servers linked together and placed in &lt;strong&gt;data centers&lt;/strong&gt;, often near &lt;strong&gt;Internet exchange points (IXPs)&lt;/strong&gt; - places where internet providers connect to exchange traffic between networks.&lt;/li&gt;
&lt;li&gt;In summary: A CDN is formed by multiple servers placed in strategic locations closer to users, enabling high-speed data delivery, and adding more optimizations on top of it to provide better security, reliability, redundancy, and more.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When to use a CDN?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;To reduce website load time:&lt;/strong&gt; A CDN distributes content closer to users, caches the content following configured rules, and also applies other optimizations such as compression of transferred data.

&lt;ul&gt;
&lt;li&gt;“Edge cache” derives from this → cache closer to users&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;To reduce bandwidth costs:&lt;/strong&gt; Before using a CDN, your origin servers would handle all requests, but now the CDN caches it, so you spend less on bandwidth and hosting.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Availability, reliability, and redundancy:&lt;/strong&gt; A CDN can also load balance traffic across several servers and apply a failover strategy to ensure hitting an available origin server, avoiding downtime.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;To improve security:&lt;/strong&gt; DDoS mitigation through a Web Application Firewall (WAF) and other optimizations.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Multi-CDN
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Multi-CDN is an approach where you use multiple CDN providers to improve reliability, performance, and global coverage. Instead of just failover, modern setups dynamically route traffic to the best-performing CDN in real time. There are trade-offs, since providers differ in APIs, caching behavior, and reporting, making it more complex to manage. Still, Multi-CDN is standard for large-scale platforms where availability and performance are critical.&lt;/li&gt;
&lt;li&gt;Only use Multi-CDN if you identify that a single CDN isn’t enough for reliability and availability: outages, geo constraints (one CDN doesn’t have strong presence close to your users), etc. Analyze all aspects before using Multi-CDN since it’s not easy to manage.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References and Suggested Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.cloudflare.com/learning/cdn/glossary/reverse-proxy/" rel="noopener noreferrer"&gt;https://www.cloudflare.com/learning/cdn/glossary/reverse-proxy/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cloudflare.com/learning/cdn/what-is-a-cdn/" rel="noopener noreferrer"&gt;https://www.cloudflare.com/learning/cdn/what-is-a-cdn/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cloudflare.com/learning/cdn/glossary/internet-exchange-point-ixp/" rel="noopener noreferrer"&gt;https://www.cloudflare.com/learning/cdn/glossary/internet-exchange-point-ixp/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cdnhandbook.com/cdn/history/" rel="noopener noreferrer"&gt;https://www.cdnhandbook.com/cdn/history/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cdnhandbook.com/multicdn/history/" rel="noopener noreferrer"&gt;https://www.cdnhandbook.com/multicdn/history/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/networking-and-content-delivery/using-multiple-content-delivery-networks-for-video-streaming-part-1/" rel="noopener noreferrer"&gt;https://aws.amazon.com/blogs/networking-and-content-delivery/using-multiple-content-delivery-networks-for-video-streaming-part-1/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>systemdesign</category>
      <category>distributedsystems</category>
      <category>network</category>
    </item>
  </channel>
</rss>
