<?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: Gouranga Das Samrat</title>
    <description>The latest articles on DEV Community by Gouranga Das Samrat (@gouranga-das-khulna).</description>
    <link>https://dev.to/gouranga-das-khulna</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%2F2193879%2Ff1ee673c-8205-4c86-b558-de345a9d4490.jpg</url>
      <title>DEV Community: Gouranga Das Samrat</title>
      <link>https://dev.to/gouranga-das-khulna</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gouranga-das-khulna"/>
    <language>en</language>
    <item>
      <title>Asymptotic Notations Explained: Big O, Omega, and Theta</title>
      <dc:creator>Gouranga Das Samrat</dc:creator>
      <pubDate>Sun, 08 Nov 2026 04:00:00 +0000</pubDate>
      <link>https://dev.to/gouranga-das-khulna/asymptotic-notations-explained-big-o-omega-and-theta-27d6</link>
      <guid>https://dev.to/gouranga-das-khulna/asymptotic-notations-explained-big-o-omega-and-theta-27d6</guid>
      <description>&lt;p&gt;&lt;em&gt;Big O is just one of three notations — here's how they all fit together.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Three Notations?
&lt;/h2&gt;

&lt;p&gt;In the previous post, we learned that Big O notation describes the &lt;strong&gt;upper bound&lt;/strong&gt; of an algorithm's complexity. But sometimes you want to say more than just "it's at most this slow." Sometimes you want to express a guarantee, a tight bound, or a lower bound.&lt;/p&gt;

&lt;p&gt;That's where the three &lt;strong&gt;asymptotic notations&lt;/strong&gt; come in:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Notation&lt;/th&gt;
&lt;th&gt;Symbol&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Describes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Big O&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;td&gt;Upper bound&lt;/td&gt;
&lt;td&gt;Worst case (at most)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Big Omega&lt;/td&gt;
&lt;td&gt;Ω&lt;/td&gt;
&lt;td&gt;Lower bound&lt;/td&gt;
&lt;td&gt;Best case (at least)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Big Theta&lt;/td&gt;
&lt;td&gt;Θ&lt;/td&gt;
&lt;td&gt;Tight bound&lt;/td&gt;
&lt;td&gt;Exact growth rate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Together, these give you a complete picture of an algorithm's behavior.&lt;/p&gt;




&lt;h2&gt;
  
  
  What "Asymptotic" Means
&lt;/h2&gt;

&lt;p&gt;The word &lt;em&gt;asymptotic&lt;/em&gt; comes from mathematics — it refers to the behavior of a function as the input approaches &lt;strong&gt;infinity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We're not interested in small inputs. When n = 5, almost any algorithm is fast. We care about what happens when n = 1,000,000 or n = 10^9. At those scales, the difference between O(n) and O(n²) is the difference between a millisecond and several hours.&lt;/p&gt;

&lt;p&gt;Asymptotic analysis ignores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Machine-specific constants&lt;/li&gt;
&lt;li&gt;Low-order terms&lt;/li&gt;
&lt;li&gt;Coefficients&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It captures only the &lt;strong&gt;essential growth rate&lt;/strong&gt; that dominates at large n.&lt;/p&gt;




&lt;h2&gt;
  
  
  Big O — Upper Bound (Worst Case)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Formal definition:&lt;/strong&gt; &lt;code&gt;f(n) = O(g(n))&lt;/code&gt; means there exist positive constants &lt;code&gt;c&lt;/code&gt; and &lt;code&gt;n₀&lt;/code&gt; such that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;f(n) ≤ c × g(n)   for all n ≥ n₀
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In plain English: from some point onwards, &lt;code&gt;g(n)&lt;/code&gt; (multiplied by a constant) is always &lt;strong&gt;at least as large&lt;/strong&gt; as &lt;code&gt;f(n)&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;f(n) = 3n + 5&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We can show this is &lt;code&gt;O(n)&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose &lt;code&gt;c = 4&lt;/code&gt;, &lt;code&gt;n₀ = 5&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;For all n ≥ 5: &lt;code&gt;3n + 5 ≤ 4n&lt;/code&gt; ✓&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Big O is the most commonly used notation because it answers the most practical question: &lt;strong&gt;"How bad can this get?"&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Common mistake
&lt;/h3&gt;

&lt;p&gt;Many people say "the time complexity is O(n²)" when they really mean "the exact time complexity is Θ(n²)." O(n²) just means "no worse than n²" — technically, an O(n) algorithm is also O(n²)! But in everyday usage, people usually mean the tightest O they can state.&lt;/p&gt;




&lt;h2&gt;
  
  
  Big Omega (Ω) — Lower Bound (Best Case)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Formal definition:&lt;/strong&gt; &lt;code&gt;f(n) = Ω(g(n))&lt;/code&gt; means there exist positive constants &lt;code&gt;c&lt;/code&gt; and &lt;code&gt;n₀&lt;/code&gt; such that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;f(n) ≥ c × g(n)   for all n ≥ n₀
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In plain English: from some point onwards, &lt;code&gt;f(n)&lt;/code&gt; is always &lt;strong&gt;at least&lt;/strong&gt; &lt;code&gt;c × g(n)&lt;/code&gt;. Omega gives you a &lt;strong&gt;floor&lt;/strong&gt; — a guarantee that the algorithm will take at least this long.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;Sorting a list of n elements requires at least O(n log n) comparisons in the &lt;strong&gt;comparison-based&lt;/strong&gt; model. This is a lower bound — no comparison-based sorting algorithm can do better. We write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sorting complexity = Ω(n log n)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  When is Ω useful?
&lt;/h3&gt;

&lt;p&gt;Omega is used in theoretical computer science to prove &lt;strong&gt;impossibility results&lt;/strong&gt; — showing that no algorithm can solve a problem faster than a certain bound.&lt;/p&gt;




&lt;h2&gt;
  
  
  Big Theta (Θ) — Tight Bound (Exact Growth Rate)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Formal definition:&lt;/strong&gt; &lt;code&gt;f(n) = Θ(g(n))&lt;/code&gt; if and only if:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;f(n) = O(g(n))   AND   f(n) = Ω(g(n))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In other words, &lt;code&gt;f(n)&lt;/code&gt; and &lt;code&gt;g(n)&lt;/code&gt; grow at &lt;strong&gt;the same rate&lt;/strong&gt; (up to constant factors). Theta is the most precise notation — it pins down both the ceiling and the floor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;Merge sort always performs &lt;code&gt;Θ(n log n)&lt;/code&gt; operations — whether the input is sorted, reversed, or random. There's no best-case shortcut.&lt;/p&gt;

&lt;p&gt;Contrast that with insertion sort:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Best case (already sorted): &lt;code&gt;Ω(n)&lt;/code&gt; — only one pass needed&lt;/li&gt;
&lt;li&gt;Worst case (reverse sorted): &lt;code&gt;O(n²)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;So insertion sort is &lt;strong&gt;not&lt;/strong&gt; &lt;code&gt;Θ(n log n)&lt;/code&gt; — it doesn't have a tight bound that works for all inputs.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Visual Intuition
&lt;/h2&gt;

&lt;p&gt;Imagine f(n) is a curve on a graph, and g(n) is a reference curve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        ↑
        |         f(n)
        |       /
O(g(n))|----/--------  upper bound (ceiling)
        |  /
Θ(g(n))|/            tight bound (matches both)
        |
Ω(g(n))|____________  lower bound (floor)
        +----------→ n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;O&lt;/code&gt; says: "f never goes above this ceiling (eventually)"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ω&lt;/code&gt; says: "f never goes below this floor (eventually)"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Θ&lt;/code&gt; says: "f is sandwiched between the same ceiling and floor"&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Little-o and Little-omega (Bonus Notations)
&lt;/h2&gt;

&lt;p&gt;There are also &lt;strong&gt;strict&lt;/strong&gt; versions of Big O and Omega:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Little-o&lt;/strong&gt; (&lt;code&gt;o(g(n))&lt;/code&gt;): f grows &lt;strong&gt;strictly slower&lt;/strong&gt; than g. The difference is that Big O allows equality; little-o does not.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Example: &lt;code&gt;2n = o(n²)&lt;/code&gt; but &lt;code&gt;n² ≠ o(n²)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Little-omega&lt;/strong&gt; (&lt;code&gt;ω(g(n))&lt;/code&gt;): f grows &lt;strong&gt;strictly faster&lt;/strong&gt; than g.&lt;/p&gt;

&lt;p&gt;These are used less often in practice but appear in advanced algorithm analysis.&lt;/p&gt;




&lt;h2&gt;
  
  
  Comparing the Notations: A Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Notation&lt;/th&gt;
&lt;th&gt;Intuition&lt;/th&gt;
&lt;th&gt;Analogy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;O(g)&lt;/td&gt;
&lt;td&gt;f ≤ c·g (eventually)&lt;/td&gt;
&lt;td&gt;f ≤ g (≤)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ω(g)&lt;/td&gt;
&lt;td&gt;f ≥ c·g (eventually)&lt;/td&gt;
&lt;td&gt;f ≥ g (≥)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Θ(g)&lt;/td&gt;
&lt;td&gt;Both O and Ω hold&lt;/td&gt;
&lt;td&gt;f = g (=)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;o(g)&lt;/td&gt;
&lt;td&gt;f/g → 0 as n→∞&lt;/td&gt;
&lt;td&gt;f &amp;lt; g (&amp;lt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ω(g)&lt;/td&gt;
&lt;td&gt;g/f → 0 as n→∞&lt;/td&gt;
&lt;td&gt;f &amp;gt; g (&amp;gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Practical Takeaways for Everyday Programming
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use Big O&lt;/strong&gt; when communicating worst-case performance (most common).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Theta&lt;/strong&gt; when you know the exact growth rate (e.g., merge sort is Θ(n log n)).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Omega&lt;/strong&gt; when you want to argue about what's &lt;em&gt;impossible&lt;/em&gt; to improve.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always state which case&lt;/strong&gt; (best/average/worst) your notation refers to, since the same algorithm can have different complexities in different scenarios.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Examples of All Three in One Algorithm
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Linear search&lt;/strong&gt; in an unsorted array looking for a target:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best case&lt;/strong&gt; (target is first element): &lt;code&gt;Ω(1)&lt;/code&gt; — found immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Worst case&lt;/strong&gt; (target is last or not present): &lt;code&gt;O(n)&lt;/code&gt; — scan the whole array.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Average case&lt;/strong&gt; (target is somewhere in the middle): &lt;code&gt;Θ(n/2) = Θ(n)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So linear search is &lt;code&gt;O(n)&lt;/code&gt;, &lt;code&gt;Ω(1)&lt;/code&gt;, and its average case is &lt;code&gt;Θ(n)&lt;/code&gt;. It does &lt;em&gt;not&lt;/em&gt; have a single tight-bound Θ that applies to all cases.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Big O (O)&lt;/strong&gt; — upper bound. The algorithm runs in at most this time. Most commonly used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Big Omega (Ω)&lt;/strong&gt; — lower bound. The algorithm takes at least this long.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Big Theta (Θ)&lt;/strong&gt; — tight bound. The algorithm's runtime is exactly this growth rate (within constants).&lt;/li&gt;
&lt;li&gt;Asymptotic analysis focuses on large n, ignoring constants and lower-order terms.&lt;/li&gt;
&lt;li&gt;An algorithm can have different complexities for best, worst, and average cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next up: we'll look at &lt;strong&gt;Best, Worst, and Average Case Analysis&lt;/strong&gt; in depth — and why all three matter depending on your use case.&lt;/p&gt;




</description>
      <category>dsa</category>
      <category>datastructures</category>
      <category>algorithms</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Time Complexity and Big O Notation: How to Measure Algorithm Speed</title>
      <dc:creator>Gouranga Das Samrat</dc:creator>
      <pubDate>Sat, 07 Nov 2026 04:00:00 +0000</pubDate>
      <link>https://dev.to/gouranga-das-khulna/time-complexity-and-big-o-notation-how-to-measure-algorithm-speed-4ddd</link>
      <guid>https://dev.to/gouranga-das-khulna/time-complexity-and-big-o-notation-how-to-measure-algorithm-speed-4ddd</guid>
      <description>&lt;p&gt;&lt;em&gt;Stop guessing whether your code is fast — learn to measure it properly.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem with "It Runs Fast on My Machine"
&lt;/h2&gt;

&lt;p&gt;When two programmers debate whose solution is better, the argument often devolves into: &lt;em&gt;"Mine ran in 0.3 seconds."&lt;/em&gt; But that's meaningless. Your colleague might have a faster CPU. The input might be small. The test might have been run under special conditions.&lt;/p&gt;

&lt;p&gt;We need a &lt;strong&gt;universal, hardware-independent way&lt;/strong&gt; to describe how fast an algorithm is. That's where &lt;strong&gt;time complexity&lt;/strong&gt; and &lt;strong&gt;Big O notation&lt;/strong&gt; come in.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is Time Complexity?
&lt;/h2&gt;

&lt;p&gt;Time complexity describes &lt;strong&gt;how the runtime of an algorithm grows&lt;/strong&gt; as the size of the input grows.&lt;/p&gt;

&lt;p&gt;We don't measure actual seconds. We count the number of &lt;strong&gt;basic operations&lt;/strong&gt; — comparisons, assignments, additions — in terms of the input size &lt;code&gt;n&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, if an algorithm does &lt;code&gt;3n + 5&lt;/code&gt; operations for an input of size &lt;code&gt;n&lt;/code&gt;, we say its time complexity is roughly &lt;strong&gt;O(n)&lt;/strong&gt; — linear.&lt;/p&gt;




&lt;h2&gt;
  
  
  Big O Notation: The Language of Efficiency
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Big O&lt;/strong&gt; (written &lt;code&gt;O(...)&lt;/code&gt;) describes an &lt;strong&gt;upper bound&lt;/strong&gt; on an algorithm's growth rate. It tells you the worst case: &lt;em&gt;no matter what, this algorithm will take at most this many steps.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The key rules of Big O:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Drop constants&lt;/strong&gt; — &lt;code&gt;O(2n)&lt;/code&gt; becomes &lt;code&gt;O(n)&lt;/code&gt;. We care about growth rate, not exact counts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drop lower-order terms&lt;/strong&gt; — &lt;code&gt;O(n² + n)&lt;/code&gt; becomes &lt;code&gt;O(n²)&lt;/code&gt;. At large n, the dominant term wins.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Different inputs get different variables&lt;/strong&gt; — If a function loops over array A (size &lt;code&gt;a&lt;/code&gt;) and array B (size &lt;code&gt;b&lt;/code&gt;), it's &lt;code&gt;O(a + b)&lt;/code&gt;, not &lt;code&gt;O(n²)&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The Big O Hierarchy (From Best to Worst)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Notation&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Constant&lt;/td&gt;
&lt;td&gt;Array index access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O(log n)&lt;/td&gt;
&lt;td&gt;Logarithmic&lt;/td&gt;
&lt;td&gt;Binary search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;Linear&lt;/td&gt;
&lt;td&gt;Linear search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O(n log n)&lt;/td&gt;
&lt;td&gt;Linearithmic&lt;/td&gt;
&lt;td&gt;Merge sort, heap sort&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O(n²)&lt;/td&gt;
&lt;td&gt;Quadratic&lt;/td&gt;
&lt;td&gt;Bubble sort, nested loops&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O(n³)&lt;/td&gt;
&lt;td&gt;Cubic&lt;/td&gt;
&lt;td&gt;Triple nested loops&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O(2ⁿ)&lt;/td&gt;
&lt;td&gt;Exponential&lt;/td&gt;
&lt;td&gt;Recursive Fibonacci (naive)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O(n!)&lt;/td&gt;
&lt;td&gt;Factorial&lt;/td&gt;
&lt;td&gt;Brute-force permutations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;As inputs grow, the difference between these complexities becomes enormous. For n = 1,000:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O(log n) → ~10 operations&lt;/li&gt;
&lt;li&gt;O(n) → 1,000 operations&lt;/li&gt;
&lt;li&gt;O(n²) → 1,000,000 operations&lt;/li&gt;
&lt;li&gt;O(2ⁿ) → 10^301 operations (the universe would end first)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  O(1) — Constant Time
&lt;/h2&gt;

&lt;p&gt;An algorithm runs in &lt;strong&gt;constant time&lt;/strong&gt; when its number of operations doesn't change with input size.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_first&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&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;arr&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;# Always 1 operation, no matter the array size
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Accessing an element by index in an array is O(1). It doesn't matter if the array has 10 elements or 10 billion — the computer jumps directly to that memory address.&lt;/p&gt;




&lt;h2&gt;
  
  
  O(n) — Linear Time
&lt;/h2&gt;

&lt;p&gt;An algorithm runs in &lt;strong&gt;linear time&lt;/strong&gt; when the number of operations scales directly with input size.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;linear_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;          &lt;span class="c1"&gt;# n iterations in the worst case
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the array doubles in size, the loop takes twice as long. Classic linear time.&lt;/p&gt;




&lt;h2&gt;
  
  
  O(n²) — Quadratic Time
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Quadratic time&lt;/strong&gt; typically comes from nested loops.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;has_duplicates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;   &lt;span class="c1"&gt;# Nested loop → O(n²)
&lt;/span&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;arr&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="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If n = 100, this does ~5,000 comparisons. If n = 1,000, it does ~500,000. Quadratic algorithms become very slow for large inputs.&lt;/p&gt;




&lt;h2&gt;
  
  
  O(log n) — Logarithmic Time
&lt;/h2&gt;

&lt;p&gt;Algorithms that &lt;strong&gt;halve the search space&lt;/strong&gt; on each step are logarithmic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;binary_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;low&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;high&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="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&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="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;low&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;high&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;mid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;low&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;high&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;target&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;mid&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;low&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mid&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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;high&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mid&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Binary search on 1,000 elements takes at most ~10 steps. On 1,000,000 elements, at most ~20 steps. That's the power of logarithmic complexity.&lt;/p&gt;




&lt;h2&gt;
  
  
  Calculating Big O: Step-by-Step
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Rule 1 — Single loops are O(n):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;do_something&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;   &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nc"&gt;O&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule 2 — Nested loops multiply:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;do_something&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nc"&gt;O&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="err"&gt;×&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;O&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="err"&gt;²&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule 3 — Sequential blocks add:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;     &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nc"&gt;O&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;     &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nc"&gt;O&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;Total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;O&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;O&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;O&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule 4 — Halving loops are O(log n):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;n&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="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;      &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nc"&gt;O&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;Big O doesn't just apply to time — it applies to &lt;strong&gt;memory&lt;/strong&gt; too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Space complexity&lt;/strong&gt; measures how much extra memory an algorithm uses as input grows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;double_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;            &lt;span class="c1"&gt;# Creates a new array of size n → O(n) space
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&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;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An in-place algorithm that modifies the input without creating new data structures uses O(1) extra space.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Mistakes When Analyzing Big O
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Mistake 1 — Including input size in space complexity.&lt;/strong&gt;&lt;br&gt;
Input always takes O(n) space. We measure the &lt;em&gt;extra&lt;/em&gt; space the algorithm uses, not the storage of the input itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 2 — Assuming nested loops are always O(n²).&lt;/strong&gt;&lt;br&gt;
If the inner loop doesn't depend on the outer loop variable, it might still be O(n²), or it might be O(n × m) for different-sized inputs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 3 — Forgetting recursion uses stack space.&lt;/strong&gt;&lt;br&gt;
A recursive function that calls itself n times uses O(n) stack space, even if each call does O(1) work.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Quick Reference
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;O(1)       &amp;lt; O(log n) &amp;lt; O(n) &amp;lt; O(n log n) &amp;lt; O(n²) &amp;lt; O(2ⁿ) &amp;lt; O(n!)
Constant     Log         Linear  N-Log-N      Quad     Exp      Factorial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you write code, always ask: &lt;em&gt;"What is the Big O of this?"&lt;/em&gt; It becomes second nature with practice.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time complexity&lt;/strong&gt; measures how operations grow with input size — not actual time in seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Big O notation&lt;/strong&gt; gives an upper bound, describing worst-case growth.&lt;/li&gt;
&lt;li&gt;Drop constants and lower-order terms.&lt;/li&gt;
&lt;li&gt;Common complexities from best to worst: O(1), O(log n), O(n), O(n log n), O(n²), O(2ⁿ), O(n!).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Space complexity&lt;/strong&gt; applies the same idea to memory usage.&lt;/li&gt;
&lt;li&gt;Nested loops multiply; sequential blocks add.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the next post, we'll go deeper into the three types of asymptotic notations — Big O, Omega (Ω), and Theta (Θ).&lt;/p&gt;




</description>
      <category>datastructures</category>
      <category>dsa</category>
      <category>algorithms</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Getting Started with Data Structures and Algorithms: A Beginner's Guide</title>
      <dc:creator>Gouranga Das Samrat</dc:creator>
      <pubDate>Sun, 01 Nov 2026 04:00:00 +0000</pubDate>
      <link>https://dev.to/gouranga-das-khulna/getting-started-with-data-structures-and-algorithms-a-beginners-guide-3774</link>
      <guid>https://dev.to/gouranga-das-khulna/getting-started-with-data-structures-and-algorithms-a-beginners-guide-3774</guid>
      <description>&lt;p&gt;&lt;em&gt;The foundation every programmer needs — explained simply.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What Are Data Structures?
&lt;/h2&gt;

&lt;p&gt;Before you can write efficient programs, you need to understand how to &lt;strong&gt;organize and store data&lt;/strong&gt;. A data structure is simply a way of arranging data in a computer's memory so that it can be used efficiently.&lt;/p&gt;

&lt;p&gt;Think of it like a kitchen. You could throw all your utensils into one big drawer — that works, but finding a specific spoon takes forever. Instead, most people use organizers: one slot for forks, one for knives, one for spoons. That's the idea behind data structures.&lt;/p&gt;

&lt;p&gt;Some common data structures include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Arrays&lt;/strong&gt; — A fixed-size, ordered collection of elements stored contiguously in memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linked Lists&lt;/strong&gt; — A chain of nodes where each node points to the next.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stacks&lt;/strong&gt; — A last-in, first-out (LIFO) structure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queues&lt;/strong&gt; — A first-in, first-out (FIFO) structure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trees&lt;/strong&gt; — Hierarchical structures with a root and child nodes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graphs&lt;/strong&gt; — A set of nodes connected by edges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hash Tables&lt;/strong&gt; — Key-value stores with near-instant lookup.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What Are Algorithms?
&lt;/h2&gt;

&lt;p&gt;An algorithm is a &lt;strong&gt;step-by-step set of instructions&lt;/strong&gt; to solve a specific problem. Every program you write uses algorithms — even something as simple as adding two numbers involves a procedure.&lt;/p&gt;

&lt;p&gt;Good algorithms are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Correct&lt;/strong&gt; — They produce the right output for all valid inputs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Efficient&lt;/strong&gt; — They use as little time and memory as possible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clear&lt;/strong&gt; — They are easy to understand and maintain.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Why Learn DSA?
&lt;/h2&gt;

&lt;p&gt;You might wonder: &lt;em&gt;"My code works fine without knowing this stuff. Why bother?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's the honest answer — &lt;strong&gt;it depends on scale&lt;/strong&gt;. A naive algorithm that works in milliseconds for 100 items might take hours for 1,000,000 items. Real-world systems process massive amounts of data constantly. DSA gives you the tools to write software that &lt;strong&gt;scales&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Beyond performance, DSA is also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Essential for coding interviews&lt;/strong&gt; — Nearly every tech company (Google, Amazon, Meta, etc.) tests DSA heavily.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A gateway to advanced topics&lt;/strong&gt; — Machine learning, compilers, databases, and operating systems are all built on DSA concepts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A way of thinking&lt;/strong&gt; — Learning DSA trains you to break down problems and think logically.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How Memory Works (The Very Basics)
&lt;/h2&gt;

&lt;p&gt;To understand data structures, you need a mental model of memory.&lt;/p&gt;

&lt;p&gt;Imagine your computer's RAM as a long strip of numbered boxes. Each box holds one unit of data (typically a byte). When your program needs to store something, it claims some boxes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Memory addresses:
[100][101][102][103][104][105]...
  5    12   3    8    7    2  ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A variable like &lt;code&gt;int x = 5&lt;/code&gt; simply puts the value &lt;code&gt;5&lt;/code&gt; at some address, say &lt;code&gt;100&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;An &lt;strong&gt;array&lt;/strong&gt; of 5 integers occupies 5 consecutive boxes. A &lt;strong&gt;linked list&lt;/strong&gt;, on the other hand, can scatter its data across non-consecutive addresses and uses pointers (addresses) to connect each piece.&lt;/p&gt;

&lt;p&gt;This fundamental difference — &lt;strong&gt;contiguous vs. non-contiguous storage&lt;/strong&gt; — is at the heart of most data structure trade-offs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Static vs. Dynamic Data Structures
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Static (e.g., Array)&lt;/th&gt;
&lt;th&gt;Dynamic (e.g., Linked List)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Size&lt;/td&gt;
&lt;td&gt;Fixed at creation&lt;/td&gt;
&lt;td&gt;Grows/shrinks at runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;td&gt;Allocated upfront&lt;/td&gt;
&lt;td&gt;Allocated on demand&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Access speed&lt;/td&gt;
&lt;td&gt;Fast (O(1) random access)&lt;/td&gt;
&lt;td&gt;Slower (O(n) traversal)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insertion/Delete&lt;/td&gt;
&lt;td&gt;Expensive (shifting)&lt;/td&gt;
&lt;td&gt;Cheap (pointer update)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Primitive vs. Non-Primitive Data Types
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Primitive&lt;/strong&gt; data types are built into the language: &lt;code&gt;int&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, &lt;code&gt;char&lt;/code&gt;, &lt;code&gt;bool&lt;/code&gt;. They hold a single value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Non-primitive&lt;/strong&gt; (or composite) data types are built from primitives. They include arrays, structures, classes, and all the data structures you'll learn throughout this series.&lt;/p&gt;




&lt;h2&gt;
  
  
  Linear vs. Non-Linear Data Structures
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Linear&lt;/strong&gt; structures store data sequentially:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Arrays&lt;/li&gt;
&lt;li&gt;Linked Lists&lt;/li&gt;
&lt;li&gt;Stacks&lt;/li&gt;
&lt;li&gt;Queues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Non-linear&lt;/strong&gt; structures organize data hierarchically or as a network:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trees&lt;/li&gt;
&lt;li&gt;Graphs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Linear structures are generally simpler to implement. Non-linear structures model real-world relationships (like file systems or social networks) more naturally.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Relationship Between Data Structures and Algorithms
&lt;/h2&gt;

&lt;p&gt;Data structures and algorithms are two sides of the same coin. The data structure you choose directly affects which algorithms you can use — and how fast they'll run.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Searching&lt;/strong&gt; is O(n) in a linked list but O(log n) in a binary search tree.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Insertion&lt;/strong&gt; is O(1) at the head of a linked list but O(n) for a sorted array.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choosing the right data structure for your problem is often the most important design decision you'll make.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Roadmap for Learning DSA
&lt;/h2&gt;

&lt;p&gt;Here's a suggested learning path (roughly what this blog series follows):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Basics&lt;/strong&gt; ← You are here&lt;/li&gt;
&lt;li&gt;Time Complexity &amp;amp; Big O Notation&lt;/li&gt;
&lt;li&gt;Asymptotic Notations&lt;/li&gt;
&lt;li&gt;Best, Worst &amp;amp; Average Case Analysis&lt;/li&gt;
&lt;li&gt;Abstract Data Types (ADTs)&lt;/li&gt;
&lt;li&gt;Arrays &amp;amp; Array Operations&lt;/li&gt;
&lt;li&gt;Linear vs. Binary Search&lt;/li&gt;
&lt;li&gt;Linked Lists (Singly, Doubly, Circular)&lt;/li&gt;
&lt;li&gt;Stacks&lt;/li&gt;
&lt;li&gt;Queues&lt;/li&gt;
&lt;li&gt;Trees &amp;amp; Binary Trees&lt;/li&gt;
&lt;li&gt;Sorting Algorithms&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;data structure&lt;/strong&gt; is a way of organizing data in memory.&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;algorithm&lt;/strong&gt; is a step-by-step procedure to solve a problem.&lt;/li&gt;
&lt;li&gt;Memory is a linear sequence of addressable boxes.&lt;/li&gt;
&lt;li&gt;Data structures are either &lt;strong&gt;static&lt;/strong&gt; (fixed size) or &lt;strong&gt;dynamic&lt;/strong&gt; (resizable).&lt;/li&gt;
&lt;li&gt;Data is either stored &lt;strong&gt;linearly&lt;/strong&gt; (arrays, lists) or &lt;strong&gt;non-linearly&lt;/strong&gt; (trees, graphs).&lt;/li&gt;
&lt;li&gt;Choosing the right structure is critical to writing efficient software.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the next post, we'll dive into &lt;strong&gt;Time Complexity and Big O Notation&lt;/strong&gt; — the language we use to talk about how fast (or slow) an algorithm really is.&lt;/p&gt;




</description>
      <category>dsa</category>
      <category>algorithms</category>
      <category>datastructures</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Google Says It's Not on Termux. It Is. Here's How to Install It.</title>
      <dc:creator>Gouranga Das Samrat</dc:creator>
      <pubDate>Mon, 26 Oct 2026 16:00:00 +0000</pubDate>
      <link>https://dev.to/gouranga-das-khulna/google-says-its-not-on-termux-it-is-heres-how-to-install-it-29im</link>
      <guid>https://dev.to/gouranga-das-khulna/google-says-its-not-on-termux-it-is-heres-how-to-install-it-29im</guid>
      <description>&lt;p&gt;Search "install Zed on Termux" and you'll get a pile of answers that boil down to "not supported." Same for Ghostty. Same for DBeaver. Same for half the tools I maintain.&lt;/p&gt;

&lt;p&gt;They're wrong, and it's not entirely Google's fault.&lt;/p&gt;

&lt;p&gt;Official install docs list Linux, macOS and Windows. Termux isn't on that list, so a search engine reading those pages concludes it doesn't exist. Meanwhile the real answer is sitting in a package repository that search engines don't index as readable pages. Zed's own docs point people to &lt;a href="https://repology.org" rel="noopener noreferrer"&gt;Repology&lt;/a&gt; for a current list of third-party packages, which tells you where the truth lives.&lt;/p&gt;

&lt;p&gt;So here's the short version of how to install things, written for people who got told "not available" and gave up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 0: make sure your Termux isn't the problem
&lt;/h2&gt;

&lt;p&gt;Half of the "package not found" reports I see come from the wrong Termux build.&lt;/p&gt;

&lt;p&gt;The official sources are F-Droid and the GitHub releases of &lt;code&gt;termux/termux-app&lt;/code&gt;. The Google Play build is deprecated: it doesn't get updates and ships outdated repository URLs. If you installed Termux from the Play Store, nothing below will work properly, and no amount of googling will fix it.&lt;/p&gt;

&lt;p&gt;Two rules when you switch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't mix sources. Termux and every plugin (Termux:API, Termux:Styling, Termux:Widget and so on) have to come from the same place, because the signatures differ.&lt;/li&gt;
&lt;li&gt;Uninstall everything first, and back up anything you care about. The Termux wiki has a backup guide.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: update before you search
&lt;/h2&gt;

&lt;p&gt;A stale package index is the second most common cause of "Unable to locate package."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pkg update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pkg upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If updates fail or the mirror looks dead, switch mirrors with the tool Termux ships for exactly this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;termux-change-repo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: check whether the package exists
&lt;/h2&gt;

&lt;p&gt;Don't ask Google. Ask the package manager.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pkg search ghostty
pkg list-all | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; dbeaver
pkg show bun
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;pkg search&lt;/code&gt; comes back empty for something that should exist, you probably haven't enabled the right repository. That's the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: enable x11-repo for anything with a window
&lt;/h2&gt;

&lt;p&gt;Termux splits GUI software into a separate repository that isn't turned on by default. Everything with a graphical interface lives there: Zed, Ghostty, DBeaver, Thonny, Bruno, CopyQ, Albert, and Playwright.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pkg &lt;span class="nb"&gt;install &lt;/span&gt;x11-repo
pkg update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the trick. A huge number of "not available" answers are really "you haven't enabled x11-repo yet."&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: install the command line tools
&lt;/h2&gt;

&lt;p&gt;These are in the main repository. No extra setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# JS and Go tooling&lt;/span&gt;
pkg &lt;span class="nb"&gt;install &lt;/span&gt;bun pnpm golangci-lint gosec govulncheck goreleaser sqlc

&lt;span class="c"&gt;# database clients&lt;/span&gt;
pkg &lt;span class="nb"&gt;install &lt;/span&gt;pgcli mycli litecli iredis mongosh

&lt;span class="c"&gt;# HTTP and load testing&lt;/span&gt;
pkg &lt;span class="nb"&gt;install &lt;/span&gt;httpie hurl k6 oha hey

&lt;span class="c"&gt;# cloud CLIs&lt;/span&gt;
pkg &lt;span class="nb"&gt;install &lt;/span&gt;azure-cli oci-cli doctl

&lt;span class="c"&gt;# git and Rust tooling&lt;/span&gt;
pkg &lt;span class="nb"&gt;install &lt;/span&gt;jujutsu bacon git-cliff cargo-audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install what you need, not the whole list. A phone has limited storage, and some of these are large.&lt;/p&gt;

&lt;p&gt;If a command isn't found after installing, check where the files went:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dpkg &lt;span class="nt"&gt;-L&lt;/span&gt; &amp;lt;package-name&amp;gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;bin/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: GUI apps need a display
&lt;/h2&gt;

&lt;p&gt;Graphical packages need something to draw on. I already wrote up that setup, from the Termux:X11 app to a full XFCE desktop, in &lt;a href="https://dev.to/gouranga-das-khulna/turn-your-android-into-a-full-desktop-no-root-needed-4fl6"&gt;Turn Your Android Into a Full Desktop, No Root Needed&lt;/a&gt;. Do that once. After that, a GUI app is just another package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pkg &lt;span class="nb"&gt;install &lt;/span&gt;dbeaver zed ghostty bruno
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are big. On a phone with limited storage, install them one at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Playwright
&lt;/h2&gt;

&lt;p&gt;Browser automation on a phone sounds ridiculous until you need it. Both packages live in x11-repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pkg &lt;span class="nb"&gt;install &lt;/span&gt;playwright python-playwright
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Python package is patched to use the system driver instead of the one bundled in the wheel, so don't &lt;code&gt;pip install playwright&lt;/code&gt; over the top of it. You'll replace the working setup with the one that doesn't run.&lt;/p&gt;

&lt;h2&gt;
  
  
  When it still doesn't work
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What you see&lt;/th&gt;
&lt;th&gt;What it usually means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Unable to locate package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;x11-repo isn't enabled, your index is stale, or the package isn't built for your architecture. Run &lt;code&gt;pkg update&lt;/code&gt;, then check &lt;code&gt;uname -m&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Bad system call&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Android's seccomp filter killed a syscall the program tried. This is a packaging bug, not something you did wrong. Open an issue.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App closes on its own after a while&lt;/td&gt;
&lt;td&gt;Android is killing background processes. Newer Android versions are aggressive about it. Search for "Termux phantom process killer" and apply the fix for your Android version.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Package installs but the command is missing&lt;/td&gt;
&lt;td&gt;The binary has a different name than the package. Use &lt;code&gt;dpkg -L&lt;/code&gt; as shown above.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Everything is weird and old&lt;/td&gt;
&lt;td&gt;You're on the Play Store build. See Step 0.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On architecture: run &lt;code&gt;uname -m&lt;/code&gt; if a package refuses to install. Most modern phones report &lt;code&gt;aarch64&lt;/code&gt;. Not every package is built for every architecture, so something can exist on one device and not on another.&lt;/p&gt;

&lt;h2&gt;
  
  
  Help fix the search results
&lt;/h2&gt;

&lt;p&gt;Search engines learn from pages. If you got something running, write about it, post the commands, and mention Termux in the title. That's the fastest way to push the "not available" answers down.&lt;/p&gt;

&lt;p&gt;If you're an upstream maintainer, adding one line to your install docs saying "Termux: &lt;code&gt;pkg install yourtool&lt;/code&gt;" does more than any SEO trick I know.&lt;/p&gt;

&lt;p&gt;Everything I maintain is listed on &lt;a href="https://repology.org/maintainer/gouranga.das.khulna%40gmail.com" rel="noopener noreferrer"&gt;Repology&lt;/a&gt;. If a package fails for you, or you want something packaged that isn't there yet, find me on &lt;a href="https://github.com/GourangaDasSamrat" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and open an issue.&lt;/p&gt;

</description>
      <category>termux</category>
      <category>android</category>
      <category>opensource</category>
      <category>rust</category>
    </item>
    <item>
      <title>DSA — Beginner to Advance: Complete Curriculum</title>
      <dc:creator>Gouranga Das Samrat</dc:creator>
      <pubDate>Mon, 26 Oct 2026 02:00:00 +0000</pubDate>
      <link>https://dev.to/gouranga-das-khulna/dsa-beginner-to-advance-complete-curriculum-1idm</link>
      <guid>https://dev.to/gouranga-das-khulna/dsa-beginner-to-advance-complete-curriculum-1idm</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Every topic, every date. Bookmark this and follow along.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Data Structures and Algorithms — the one thing that separates a developer who passes interviews from one who doesn't. More importantly, it separates code that works from code that &lt;em&gt;scales&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This series takes you from zero to interview-ready, week by week.&lt;/p&gt;




&lt;h2&gt;
  
  
  🟢 Beginner — Foundations
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Topic&lt;/th&gt;
&lt;th&gt;Publish Date&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Getting Started with Data Structures and Algorithms&lt;/td&gt;
&lt;td&gt;Nov 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Time Complexity and Big O Notation&lt;/td&gt;
&lt;td&gt;Nov 7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Asymptotic Notations Explained&lt;/td&gt;
&lt;td&gt;Nov 8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Best, Worst, and Average Case Analysis&lt;/td&gt;
&lt;td&gt;Nov 14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Abstract Data Types (ADTs)&lt;/td&gt;
&lt;td&gt;Nov 15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Arrays and Array Operations&lt;/td&gt;
&lt;td&gt;Nov 21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Linear Search vs. Binary Search&lt;/td&gt;
&lt;td&gt;Nov 22&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Linked Lists: Dynamic Memory and Pointers&lt;/td&gt;
&lt;td&gt;Nov 28&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Circular and Doubly Linked Lists&lt;/td&gt;
&lt;td&gt;Nov 29&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Stacks: The LIFO Structure&lt;/td&gt;
&lt;td&gt;Dec 5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;Queues: The FIFO Structure&lt;/td&gt;
&lt;td&gt;Dec 6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🔵 Intermediate — Core Data Structures
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Topic&lt;/th&gt;
&lt;th&gt;Publish Date&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;Trees and Binary Trees: Hierarchical Data&lt;/td&gt;
&lt;td&gt;Dec 12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;Binary Search Trees: Ordered Trees&lt;/td&gt;
&lt;td&gt;Dec 13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;Sorting Algorithms: From Bubble Sort to Merge Sort&lt;/td&gt;
&lt;td&gt;Dec 19&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;Hash Tables: The Magic Behind O(1) Lookups&lt;/td&gt;
&lt;td&gt;Dec 20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;Graphs: The Most Powerful Data Structure&lt;/td&gt;
&lt;td&gt;Dec 26&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;Heaps and Priority Queues&lt;/td&gt;
&lt;td&gt;Dec 27&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;AVL Trees and Red-Black Trees&lt;/td&gt;
&lt;td&gt;Jan 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;Tries (Prefix Trees): Data Structure Behind Autocomplete&lt;/td&gt;
&lt;td&gt;Jan 3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🔴 Advanced — Algorithms
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Topic&lt;/th&gt;
&lt;th&gt;Publish Date&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;Dynamic Programming: Breaking Hard Problems&lt;/td&gt;
&lt;td&gt;Jan 9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;Advanced Graph Algorithms: MST, SCC, and More&lt;/td&gt;
&lt;td&gt;Jan 10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;Segment Trees and Fenwick Trees&lt;/td&gt;
&lt;td&gt;Jan 16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;Greedy Algorithms: Locally Optimal Choices&lt;/td&gt;
&lt;td&gt;Jan 17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;Backtracking: Systematic Search Through All Possibilities&lt;/td&gt;
&lt;td&gt;Jan 23&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;String Algorithms: KMP, Rabin-Karp, and Z-Algorithm&lt;/td&gt;
&lt;td&gt;Jan 24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;Divide and Conquer: Split, Solve, Combine&lt;/td&gt;
&lt;td&gt;Jan 30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;td&gt;Advanced Hashing and Bloom Filters&lt;/td&gt;
&lt;td&gt;Jan 31&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  📅 How to Follow This Series
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;New posts drop &lt;strong&gt;every Saturday &amp;amp; Sunday&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Start from #1 — every concept builds on the previous&lt;/li&gt;
&lt;li&gt;Each post includes examples and complexity analysis&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Follow to get notified when each post drops.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dsa</category>
      <category>learning</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>Zed, Bun, Ghostty: Hard Mode Packaging on Android</title>
      <dc:creator>Gouranga Das Samrat</dc:creator>
      <pubDate>Sun, 25 Oct 2026 16:00:00 +0000</pubDate>
      <link>https://dev.to/gouranga-das-khulna/zed-bun-ghostty-codex-hard-mode-packaging-on-android-5f3d</link>
      <guid>https://dev.to/gouranga-das-khulna/zed-bun-ghostty-codex-hard-mode-packaging-on-android-5f3d</guid>
      <description>&lt;p&gt;The hard tier is software that assumes a full desktop Linux box. GPU loaders, shared memory, process watchers, JIT engines, and dependency trees so big nobody on the upstream team has read all of them.&lt;/p&gt;

&lt;p&gt;I maintain a good chunk of that tier for Termux. Here's what the patches look like, and what each one is really saying.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bun: nine patches, three of them syscalls
&lt;/h2&gt;

&lt;p&gt;Bun has the longest patch stack I deal with regularly. The filenames tell most of the story:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Patch&lt;/th&gt;
&lt;th&gt;What it fixes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0001&lt;/code&gt; default to symlink install backend&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;bun install&lt;/code&gt; uses symlinks by default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0002&lt;/code&gt; remove Werror and unsupported flags&lt;/td&gt;
&lt;td&gt;Build stops failing on warnings and flags this toolchain lacks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0003&lt;/code&gt; disable close_range&lt;/td&gt;
&lt;td&gt;Skip a syscall Android may refuse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0004&lt;/code&gt; set termux tmpdir&lt;/td&gt;
&lt;td&gt;Temp files go to &lt;code&gt;$PREFIX/tmp&lt;/code&gt; instead of &lt;code&gt;/tmp&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0005&lt;/code&gt; fix bash completion empty regex&lt;/td&gt;
&lt;td&gt;Completion generation no longer chokes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0006&lt;/code&gt; fix symlink bin chmod&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;chmod&lt;/code&gt; on symlinked binaries stops failing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0007&lt;/code&gt; disable pidfd_open&lt;/td&gt;
&lt;td&gt;Another filtered syscall, another fallback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0008&lt;/code&gt; disable statx&lt;/td&gt;
&lt;td&gt;Same story&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0009&lt;/code&gt; android link rust as machine code&lt;/td&gt;
&lt;td&gt;Linking fix for the Rust-built part on Android&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Read that list again. Three of nine are the same bug wearing different hats: the runtime probes a modern kernel feature, Android's filter objects. Upstream already carries fallbacks for old kernels, so the patch is mostly "pretend you're on an old kernel."&lt;/p&gt;

&lt;p&gt;I like this class of fix because it's small and honest. It doesn't fork the project. It doesn't rewrite anything. It switches off a fast path and lets the existing slow path run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ghostty: GPU terminal, six patches
&lt;/h2&gt;

&lt;p&gt;Ghostty is a good example of a project that makes zero Android assumptions, which is fair, it's a desktop terminal. Six patches got it running:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Patch&lt;/th&gt;
&lt;th&gt;Problem it solves&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0001&lt;/code&gt; dcimgui use desktop GL loader on android&lt;/td&gt;
&lt;td&gt;The UI layer picks the wrong GL loader for the target&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0002&lt;/code&gt; kitty graphics no shm_open on android&lt;/td&gt;
&lt;td&gt;Bionic has no &lt;code&gt;shm_open&lt;/code&gt;, so the image protocol needs another path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0003&lt;/code&gt; global skip xev io_uring probe on android&lt;/td&gt;
&lt;td&gt;The &lt;code&gt;io_uring&lt;/code&gt; feature probe is a &lt;code&gt;SIGSYS&lt;/code&gt; waiting to happen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0004&lt;/code&gt; android default shell, home and /bin/sh&lt;/td&gt;
&lt;td&gt;Sensible defaults when there's no &lt;code&gt;/bin/sh&lt;/code&gt; and no standard home&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0005&lt;/code&gt; os file android tmpdir&lt;/td&gt;
&lt;td&gt;Yes, &lt;code&gt;/tmp&lt;/code&gt; again&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0006&lt;/code&gt; termio android process watcher without pidfd&lt;/td&gt;
&lt;td&gt;Watch child processes without &lt;code&gt;pidfd&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you've read part one, you've already seen four of those problems. Only the GL loader is new. The same short list of Android quirks keeps showing up in completely unrelated codebases, and that's the main thing I've learned. After the first dozen ports, a failure stops being a mystery and becomes a checklist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zed: when the dependency tree is the problem
&lt;/h2&gt;

&lt;p&gt;Zed is where the approach changes. The editor itself is fine. The trouble is in the crates underneath it. My recipe carries patches for &lt;code&gt;alacritty_terminal&lt;/code&gt;, &lt;code&gt;ipc-channel&lt;/code&gt;, &lt;code&gt;trash&lt;/code&gt;, &lt;code&gt;wasmtime-internal-jit-icache-coherence&lt;/code&gt;, &lt;code&gt;wayland-cursor&lt;/code&gt;, &lt;code&gt;x11rb-protocol&lt;/code&gt; and &lt;code&gt;zed-scap&lt;/code&gt;, plus a workspace-level patch that wires them together.&lt;/p&gt;

&lt;p&gt;Patching code that lives in other people's crates is where Rust packaging gets fiddly. The workable approach is to vendor the dependencies, patch the vendored sources, and tell Cargo to build from them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# illustrative shape; real recipes differ per package&lt;/span&gt;
termux_step_pre_configure&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    termux_setup_rust

    &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TERMUX_PKG_SRCDIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    cargo vendor &lt;span class="nt"&gt;--locked&lt;/span&gt; vendor &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null

    &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; .cargo
    &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .cargo/config.toml &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
        [source.crates-io]
        replace-with = "vendored-sources"

        [source.vendored-sources]
        directory = "vendor"
&lt;/span&gt;&lt;span class="no"&gt;    EOF

&lt;/span&gt;    &lt;span class="k"&gt;for &lt;/span&gt;diff &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TERMUX_PKG_BUILDER_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.vendor.diff&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
        &lt;/span&gt;patch &lt;span class="nt"&gt;-p1&lt;/span&gt; &amp;lt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$diff&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;done&lt;/span&gt;

    &lt;span class="c"&gt;# Cargo verifies vendored files against recorded checksums.&lt;/span&gt;
    &lt;span class="c"&gt;# Patched files fail that check, so clear the file list.&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;f &lt;span class="k"&gt;in &lt;/span&gt;vendor/&lt;span class="k"&gt;*&lt;/span&gt;/.cargo-checksum.json&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
        &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/"files":{[^}]*}/"files":{}/'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The checksum step is the one that eats an afternoon. You patch a vendored crate, run the build, and Cargo tells you a file changed. It's correct, you did change it. Emptying the &lt;code&gt;files&lt;/code&gt; map in &lt;code&gt;.cargo-checksum.json&lt;/code&gt; is how you tell it that's intentional.&lt;/p&gt;

&lt;p&gt;Zed also ships a &lt;code&gt;zed-shim.sh&lt;/code&gt;. I use shims when an app needs environment set up before it starts, and I'd rather wrap it than fork it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!@TERMUX_PREFIX@/bin/sh&lt;/span&gt;
&lt;span class="c"&gt;# illustrative shape&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TMPDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TMPDIR&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;@TERMUX_PREFIX@/tmp&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="s2"&gt;"@TERMUX_PREFIX@/opt/example/bin/example"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bruno has the same kind of shim, alongside two patches of its own: one to lazy-load &lt;code&gt;node-pty&lt;/code&gt;, and one so the machine-id lookup doesn't try to execute anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser automation on a phone
&lt;/h2&gt;

&lt;p&gt;Playwright is the one that makes people do a double take. Browser automation needs a browser, a driver, and a runtime that agree with each other. The recipe has &lt;code&gt;android-support.patch&lt;/code&gt;, two Firefox patches (&lt;code&gt;firefox-support&lt;/code&gt; and &lt;code&gt;firefox-single-window&lt;/code&gt;), and a path-matching fix. The Python side, &lt;code&gt;python-playwright&lt;/code&gt;, has a &lt;code&gt;use-system-driver.patch&lt;/code&gt;, so it talks to the driver installed on the system instead of the one bundled in the wheel.&lt;/p&gt;

&lt;p&gt;That bundled driver is a good example of a general rule: anything that ships its own prebuilt binary is a candidate for "swap it for the system one."&lt;/p&gt;

&lt;h2&gt;
  
  
  The small stuff matters too
&lt;/h2&gt;

&lt;p&gt;Not every patch is dramatic. Some of my favorites are tiny:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;numbat&lt;/code&gt; disables Android plotting support, because the plotting backend has no business running here.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pnpm&lt;/code&gt; builds &lt;code&gt;sha2&lt;/code&gt; without asm.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;uutils-coreutils&lt;/code&gt; carries fixes for 32-bit devices: an &lt;code&gt;auxv&lt;/code&gt; fix, a 32-bit &lt;code&gt;stat&lt;/code&gt; mode fix in &lt;code&gt;tail&lt;/code&gt;, and a small &lt;code&gt;syncfs.c&lt;/code&gt;. A lot of phones are still 32-bit, and nobody tests for them.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;thonny&lt;/code&gt; needed four patches around &lt;code&gt;mypy&lt;/code&gt;, shell detection and path opening.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;albert&lt;/code&gt; has six patches for its XDG handling, system utilities and plugin loading.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I'd tell anyone starting
&lt;/h2&gt;

&lt;p&gt;Read the failure literally. &lt;code&gt;ENOENT&lt;/code&gt; is a path problem. &lt;code&gt;SIGSYS&lt;/code&gt; is a syscall problem. A missing symbol is a Bionic gap. Most of the work is classifying the failure correctly.&lt;/p&gt;

&lt;p&gt;Prefer small, guarded patches. Wrap the change in &lt;code&gt;__ANDROID__&lt;/code&gt; or the language equivalent, and keep it narrow. Those are patches upstream might actually accept.&lt;/p&gt;

&lt;p&gt;Shim before you fork. An env-setting wrapper script is easier to maintain than a diverged repository.&lt;/p&gt;

&lt;p&gt;And be honest about the cost. Every version bump is a rebase. Nine patches on Bun means nine things that can break on the next release. That's the part nobody writes blog posts about, and it's most of the job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to find it
&lt;/h2&gt;

&lt;p&gt;Everything I maintain is tracked on &lt;a href="https://repology.org/maintainer/gouranga.das.khulna%40gmail.com" rel="noopener noreferrer"&gt;Repology&lt;/a&gt;, and I'm on &lt;a href="https://github.com/GourangaDasSamrat" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; if you want to see the work, open an issue, or tell me which package you'd like to see next.&lt;/p&gt;

&lt;p&gt;Termux has limits. I just don't agree with where most people think they are.&lt;/p&gt;

</description>
      <category>termux</category>
      <category>rust</category>
      <category>android</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Rate Limiter Designs</title>
      <dc:creator>Gouranga Das Samrat</dc:creator>
      <pubDate>Sun, 25 Oct 2026 04:00:00 +0000</pubDate>
      <link>https://dev.to/gouranga-das-khulna/rate-limiter-designs-10h0</link>
      <guid>https://dev.to/gouranga-das-khulna/rate-limiter-designs-10h0</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Rate limiting is a classic system design topic. Know ALL the algorithms — not just one.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why Rate Limit?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Protect APIs from abuse (DDoS, scraping)&lt;/li&gt;
&lt;li&gt;Ensure fair usage across clients&lt;/li&gt;
&lt;li&gt;Protect downstream services from overload&lt;/li&gt;
&lt;li&gt;Enforce business rules (free tier: 100 req/day)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Algorithm 1: Fixed Window Counter
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Window: 00:00 → 01:00
Limit: 100 requests per hour

User sends request at 00:59:59 → counter = 100 → allowed ✅
User sends request at 01:00:01 → new window, counter = 1 → allowed ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Problem: Boundary burst attack&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00:59 → 100 requests (at limit)
01:00 → 100 requests (new window reset)
Total: 200 requests in 2 seconds 💥
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ Simple, low memory&lt;br&gt;
❌ Allows 2x traffic at window boundaries&lt;/p&gt;


&lt;h2&gt;
  
  
  Algorithm 2: Sliding Window Log
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Store timestamp of every request in a log (sorted set)&lt;/li&gt;
&lt;li&gt;On new request: remove timestamps older than window, count remaining&lt;/li&gt;
&lt;li&gt;If count &amp;lt; limit → allow
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Limit: 5 requests per minute
Log: [00:00:10, 00:00:30, 00:00:45, 00:00:55]
New request at 00:01:20:
  Remove entries &amp;lt; 00:00:20 → log: [00:00:30, 00:00:45, 00:00:55]
  Count = 3, limit = 5 → allow ✅
  Add 00:01:20 to log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;✅ Perfectly accurate, no boundary problem&lt;br&gt;
❌ High memory — store every request timestamp&lt;br&gt;
❌ Doesn't scale to millions of users&lt;/p&gt;


&lt;h2&gt;
  
  
  Algorithm 3: Sliding Window Counter (Hybrid)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Combine fixed window simplicity with sliding accuracy&lt;/li&gt;
&lt;li&gt;Use weighted count from current + previous window
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Limit: 100 req/min
Previous window count: 80
Current window count: 30
Time into current window: 25% elapsed

Estimated rate = 80 * (1 - 0.25) + 30 = 90 → under limit ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;✅ Memory efficient (only 2 counters)&lt;br&gt;
✅ Good approximation (Cloudflare uses this)&lt;br&gt;
❌ Slightly approximate, not exact&lt;/p&gt;


&lt;h2&gt;
  
  
  Algorithm 4: Token Bucket 🪣
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bucket capacity: 10 tokens
Refill rate: 1 token/second

Each request consumes 1 token
If bucket empty → reject request

Bucket refills continuously at fixed rate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Allows &lt;strong&gt;bursts&lt;/strong&gt; up to bucket capacity&lt;/li&gt;
&lt;li&gt;Smooth average rate enforced over time&lt;/li&gt;
&lt;li&gt;✅ Natural burst handling (users expect some burst tolerance)&lt;/li&gt;
&lt;li&gt;✅ Low memory (capacity + current tokens + last refill time)&lt;/li&gt;
&lt;li&gt;✅ Used by: AWS API Gateway, Stripe, most cloud providers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Implementation with Redis:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;last_refill&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tokens&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="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;allow&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="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Algorithm 5: Leaky Bucket 🚿
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Requests enter a queue (bucket)
Queue drains at fixed rate (the "leak")
If queue full → reject new requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Output is ALWAYS at fixed rate — no bursts&lt;/li&gt;
&lt;li&gt;✅ Smooths out traffic perfectly&lt;/li&gt;
&lt;li&gt;✅ Good for protecting downstream services that can't handle spikes&lt;/li&gt;
&lt;li&gt;❌ Requests queue up — adds latency&lt;/li&gt;
&lt;li&gt;❌ Burst requests at start of window get queued, not rejected fast&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Difference from Token Bucket:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Token Bucket: allows bursts at token bucket capacity&lt;/li&gt;
&lt;li&gt;Leaky Bucket: forces steady rate, queues or drops excess&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Where to Implement Rate Limiting
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Edge/CDN&lt;/td&gt;
&lt;td&gt;Cloudflare Rate Limiting&lt;/td&gt;
&lt;td&gt;Best for DDoS protection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Gateway&lt;/td&gt;
&lt;td&gt;AWS API Gateway, Kong&lt;/td&gt;
&lt;td&gt;Easy to configure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application&lt;/td&gt;
&lt;td&gt;Redis-based counter&lt;/td&gt;
&lt;td&gt;Most flexible, custom logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;pg_rate_limit, etc&lt;/td&gt;
&lt;td&gt;Rare, usually too late&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;In distributed systems:&lt;/strong&gt; Use a centralized store (Redis) so all API server instances share the same counter.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rate Limit Response
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;HTTP 429 Too Many Requests
Headers:
  X-RateLimit-Limit: 100
  X-RateLimit-Remaining: 0
  X-RateLimit-Reset: 1704067260
  Retry-After: 60
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ⚖️ Which Algorithm to Choose?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Algorithm&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Simple API limits&lt;/td&gt;
&lt;td&gt;Fixed Window (simple to implement)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accurate sliding limits&lt;/td&gt;
&lt;td&gt;Sliding Window Counter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Allow burst, limit average&lt;/td&gt;
&lt;td&gt;Token Bucket ✅ (most common answer)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Smooth output rate&lt;/td&gt;
&lt;td&gt;Leaky Bucket&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Default interview answer:&lt;/strong&gt; Token Bucket — it's what AWS uses and it handles real-world traffic patterns well.&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>distributedsystems</category>
      <category>api</category>
      <category>backend</category>
    </item>
    <item>
      <title>Termux Isn't Linux. I Packaged 70+ Tools for It Anyway</title>
      <dc:creator>Gouranga Das Samrat</dc:creator>
      <pubDate>Sat, 24 Oct 2026 11:13:39 +0000</pubDate>
      <link>https://dev.to/gouranga-das-khulna/termux-isnt-linux-i-packaged-70-tools-for-it-anyway-1mfg</link>
      <guid>https://dev.to/gouranga-das-khulna/termux-isnt-linux-i-packaged-70-tools-for-it-anyway-1mfg</guid>
      <description>&lt;p&gt;Termux looks like Linux right up until you try to build something that matters.&lt;/p&gt;

&lt;p&gt;You get a shell, a package manager, a familiar prompt. Then you clone a project, run its build script, and watch it die on a path that doesn't exist, a syscall you aren't allowed to make, or a libc function that was never there.&lt;/p&gt;

&lt;p&gt;I got tired of watching it fail, so I started fixing it. I now maintain 70+ packages for Termux, including things people assume can't run on a phone: Bun, Zed, Ghostty, Playwright, Codex, DBeaver. This is the first of two posts. This one is about the wall. The next one is about what's on the other side.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Termux actually is
&lt;/h2&gt;

&lt;p&gt;Termux is not a Linux distro that happens to run on Android. It's a userland compiled against Android's own C library, Bionic, living inside a private app directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$PREFIX&lt;/span&gt;
&lt;span class="go"&gt;/data/data/com.termux/files/usr

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; /bin /tmp /usr
&lt;span class="go"&gt;ls: cannot access '/bin': No such file or directory
ls: cannot access '/tmp': No such file or directory
ls: cannot access '/usr': No such file or directory
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three consequences, and every build failure I've fixed traces back to one of them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No glibc.&lt;/strong&gt; Bionic is smaller and has gaps. It has no &lt;code&gt;shm_open&lt;/code&gt;, for example, and a lot of upstream code never checks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No standard filesystem layout.&lt;/strong&gt; Everything lives under &lt;code&gt;$PREFIX&lt;/code&gt;. You can't create &lt;code&gt;/tmp&lt;/code&gt; or &lt;code&gt;/bin/sh&lt;/code&gt; without root, and I'm not interested in solutions that need root.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A syscall filter.&lt;/strong&gt; Android sandboxes apps with seccomp. Depending on the Android version, calling a filtered syscall doesn't return &lt;code&gt;ENOSYS&lt;/code&gt;. It kills the process with &lt;code&gt;SIGSYS&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There is a route where you drag glibc along with a runner and execute stock Linux binaries. It works for some things. I'd rather ship native packages, so the person on the other end types &lt;code&gt;pkg install&lt;/code&gt; and moves on with their day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 1: rewrite the paths, don't emulate them
&lt;/h2&gt;

&lt;p&gt;Upstream code hardcodes &lt;code&gt;/usr/share/whatever&lt;/code&gt; and &lt;code&gt;/etc/whatever&lt;/code&gt;. Fighting that at runtime is a losing game. Patch it at build time, once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;termux_step_pre_configure&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"s|&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;/usr/share/example-tool&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$TERMUX_PREFIX&lt;/span&gt;&lt;span class="s2"&gt;/share/example-tool&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;|"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        src/paths.c
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the same placeholder shows up in several files, I use a token like &lt;code&gt;@TERMUX_PREFIX@&lt;/code&gt; in a patch and substitute it in the recipe, so the patch stays readable and the real path gets injected at build time.&lt;/p&gt;

&lt;p&gt;Shebangs are the same problem in a different costume. &lt;code&gt;#!/bin/bash&lt;/code&gt; and &lt;code&gt;#!/usr/bin/env python&lt;/code&gt; point at nothing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;termux-fix-shebang script.sh
&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt; script.sh
&lt;span class="c"&gt;# #!/data/data/com.termux/files/usr/bin/bash&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;termux-exec&lt;/code&gt; papers over some of this at runtime, but it only covers &lt;code&gt;exec&lt;/code&gt;. It does nothing when a program calls &lt;code&gt;open("/tmp/foo")&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 2: hardcoded temp dirs
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;/tmp&lt;/code&gt; is the most common offender. Bun needed a patch for it. Ghostty needed one. The shape is always the same:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-    const char *dir = "/tmp";
&lt;/span&gt;&lt;span class="gi"&gt;+    const char *dir = getenv("TMPDIR");
+    if (!dir || !*dir) dir = "@TERMUX_PREFIX@/tmp";
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tiny diff. Takes an hour to find, because the failure shows up three layers away from the cause, usually as "permission denied" on a path you didn't know the program was using.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 3: syscalls, or how to get killed by a feature probe
&lt;/h2&gt;

&lt;p&gt;This is the nasty one. Modern runtimes try fast paths: &lt;code&gt;close_range&lt;/code&gt;, &lt;code&gt;pidfd_open&lt;/code&gt;, &lt;code&gt;statx&lt;/code&gt;, &lt;code&gt;io_uring&lt;/code&gt;. On a normal Linux box, an old kernel answers &lt;code&gt;ENOSYS&lt;/code&gt; and the program falls back to the slow path. On Android, the filter can answer with &lt;code&gt;SIGSYS&lt;/code&gt; and your build dies with "Bad system call".&lt;/p&gt;

&lt;p&gt;Bun needed three separate patches for this: &lt;code&gt;close_range&lt;/code&gt;, &lt;code&gt;pidfd_open&lt;/code&gt;, and &lt;code&gt;statx&lt;/code&gt;. Ghostty needed two: one to skip an &lt;code&gt;io_uring&lt;/code&gt; probe, and one to replace its pidfd-based process watcher.&lt;/p&gt;

&lt;p&gt;The fix is almost boring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; // illustrative shape, not a verbatim patch
&lt;span class="gd"&gt;-    if (close_range(3, ~0U, 0) == 0) return;
&lt;/span&gt;&lt;span class="gi"&gt;+#ifndef __ANDROID__
+    if (close_range(3, ~0U, 0) == 0) return;
+#endif
+    close_fds_slowly(3);
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Upstream already maintains the fallback because it supports old kernels. All I do is make Android look like an old kernel. Compile the fast path out, let the slow path run, move on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 4: let the toolchain do the cross-compiling
&lt;/h2&gt;

&lt;p&gt;Most of my packages are Go and Rust, and that's not an accident. Both cross-compile cleanly, and Termux's build system ships helpers for them. A Go CLI recipe is nearly trivial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;TERMUX_PKG_HOMEPAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://github.com/example/example-tool
&lt;span class="nv"&gt;TERMUX_PKG_DESCRIPTION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Example Go CLI"&lt;/span&gt;
&lt;span class="nv"&gt;TERMUX_PKG_LICENSE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"MIT"&lt;/span&gt;
&lt;span class="nv"&gt;TERMUX_PKG_MAINTAINER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"@GourangaDasSamrat"&lt;/span&gt;
&lt;span class="nv"&gt;TERMUX_PKG_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.2.3
&lt;span class="nv"&gt;TERMUX_PKG_SRCURL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://github.com/example/example-tool/archive/v&lt;span class="nv"&gt;$TERMUX_PKG_VERSION&lt;/span&gt;.tar.gz
&lt;span class="nv"&gt;TERMUX_PKG_SHA256&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;sha256&amp;gt;
&lt;span class="nv"&gt;TERMUX_PKG_BUILD_IN_SRC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true

&lt;/span&gt;termux_step_make&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    termux_setup_golang
    go build &lt;span class="nt"&gt;-trimpath&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; example-tool ./cmd/example-tool
&lt;span class="o"&gt;}&lt;/span&gt;

termux_step_make_install&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-Dm755&lt;/span&gt; example-tool &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TERMUX_PREFIX&lt;/span&gt;&lt;span class="s2"&gt;/bin/example-tool"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rust is the same idea with &lt;code&gt;termux_setup_rust&lt;/code&gt;, plus a pile of crate-specific pain I'll get to in part two.&lt;/p&gt;

&lt;p&gt;Python packages with native extensions are their own category. &lt;code&gt;pip install&lt;/code&gt; on a phone means compiling Rust or C on a phone, which is a bad time. That's why things like &lt;code&gt;python-rpds-py&lt;/code&gt;, &lt;code&gt;python-pynacl&lt;/code&gt;, &lt;code&gt;python-crc32c&lt;/code&gt; and &lt;code&gt;python-jellyfish&lt;/code&gt; exist as packages: one build on my side, an instant install on yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this buys you
&lt;/h2&gt;

&lt;p&gt;Once the wall is down, a phone stops being a toy terminal. From Termux you can now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;talk to databases with &lt;code&gt;pgcli&lt;/code&gt;, &lt;code&gt;mycli&lt;/code&gt;, &lt;code&gt;litecli&lt;/code&gt;, &lt;code&gt;iredis&lt;/code&gt; and &lt;code&gt;mongosh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;drive cloud accounts with &lt;code&gt;azure-cli&lt;/code&gt;, &lt;code&gt;oci-cli&lt;/code&gt; and &lt;code&gt;doctl&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;lint and secure Go code with &lt;code&gt;golangci-lint&lt;/code&gt;, &lt;code&gt;gosec&lt;/code&gt; and &lt;code&gt;govulncheck&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;cut releases with &lt;code&gt;goreleaser&lt;/code&gt; and &lt;code&gt;git-cliff&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;load test an API with &lt;code&gt;k6&lt;/code&gt;, &lt;code&gt;oha&lt;/code&gt; or &lt;code&gt;hey&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;use &lt;code&gt;jujutsu&lt;/code&gt; instead of git, if you're that kind of person&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of those work out of the box. Each one has a recipe, and some have a pile of patches behind them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I bother
&lt;/h2&gt;

&lt;p&gt;Because "Termux can't do that" is almost never true. It's usually "nobody has patched &lt;code&gt;/tmp&lt;/code&gt; yet."&lt;/p&gt;

&lt;p&gt;If you want to see the full list of what I maintain, it's on &lt;a href="https://repology.org/maintainer/gouranga.das.khulna%40gmail.com" rel="noopener noreferrer"&gt;Repology&lt;/a&gt;. The recipes and patches live in the Termux packages repo, and my other work is on &lt;a href="https://github.com/GourangaDasSamrat" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Part two gets into the hard cases: a JavaScript runtime with nine patches, a GPU-accelerated terminal, and a code editor whose dependency tree needed surgery.&lt;/p&gt;

&lt;p&gt;If you've hit a &lt;code&gt;Bad system call&lt;/code&gt; in Termux and couldn't figure out why, drop it in the comments. I've probably seen it.&lt;/p&gt;

</description>
      <category>termux</category>
      <category>android</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Leader Election</title>
      <dc:creator>Gouranga Das Samrat</dc:creator>
      <pubDate>Sat, 24 Oct 2026 04:00:00 +0000</pubDate>
      <link>https://dev.to/gouranga-das-khulna/leader-election-46g3</link>
      <guid>https://dev.to/gouranga-das-khulna/leader-election-46g3</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;In a distributed system, sometimes exactly ONE node must act as the "leader". How do you pick it?&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why Leader Election?
&lt;/h2&gt;

&lt;p&gt;Some tasks must run on exactly one node:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sending scheduled emails (don't send twice)&lt;/li&gt;
&lt;li&gt;Coordinating distributed transactions&lt;/li&gt;
&lt;li&gt;Acting as primary in a database cluster&lt;/li&gt;
&lt;li&gt;Processing a specific partition of a queue&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Nodes can crash. The leader must be detected as dead and a new one elected.&lt;/p&gt;




&lt;h2&gt;
  
  
  Algorithms
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Bully Algorithm
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Node with highest ID wins&lt;/li&gt;
&lt;li&gt;When a node thinks the leader is dead → sends election messages to all higher-ID nodes&lt;/li&gt;
&lt;li&gt;If no response → declares itself leader&lt;/li&gt;
&lt;li&gt;✅ Simple | ❌ Many messages, assumes ordered IDs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Raft Consensus
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Nodes vote for a leader candidate&lt;/li&gt;
&lt;li&gt;Candidate needs majority vote (quorum)&lt;/li&gt;
&lt;li&gt;Leader sends heartbeats; if missed → new election&lt;/li&gt;
&lt;li&gt;✅ Well-proven, used in production systems&lt;/li&gt;
&lt;li&gt;Used by: etcd, CockroachDB, TiKV&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ZooKeeper / etcd-based Leader Election (Practical)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;All nodes race to create an ephemeral node:
  /election/leader

First node to create it → wins leadership
Others watch the node for deletion (leader crash)
On deletion → race again
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;✅ Battle-tested, you don't implement the algorithm yourself&lt;/li&gt;
&lt;li&gt;✅ Ephemeral nodes auto-delete when the session dies (crash detection built in)&lt;/li&gt;
&lt;li&gt;❌ Adds ZooKeeper/etcd as dependency&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Split-Brain Problem
&lt;/h2&gt;

&lt;p&gt;What if the network partitions and both halves elect a leader?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two leaders making conflicting decisions = data corruption&lt;/li&gt;
&lt;li&gt;Solution: &lt;strong&gt;quorum&lt;/strong&gt; — leader must maintain majority (N/2 + 1) connections&lt;/li&gt;
&lt;li&gt;If minority partition can't reach quorum → steps down&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Real-World Usage
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;System&lt;/th&gt;
&lt;th&gt;Leader Election&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Kafka&lt;/td&gt;
&lt;td&gt;ZooKeeper (legacy) / KRaft (new) for controller&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Elasticsearch&lt;/td&gt;
&lt;td&gt;Zen Discovery / Raft-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redis Sentinel&lt;/td&gt;
&lt;td&gt;Sentinel quorum for primary election&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL HA&lt;/td&gt;
&lt;td&gt;Patroni uses etcd/ZooKeeper&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>systemdesign</category>
      <category>api</category>
      <category>backend</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>Distributed Cron Jobs</title>
      <dc:creator>Gouranga Das Samrat</dc:creator>
      <pubDate>Sun, 18 Oct 2026 04:00:00 +0000</pubDate>
      <link>https://dev.to/gouranga-das-khulna/distributed-cron-jobs-24ol</link>
      <guid>https://dev.to/gouranga-das-khulna/distributed-cron-jobs-24ol</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Regular cron runs on one server. What happens when you have 10 servers? Each runs the job 10 times. That's a problem.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Single server cron: ✅ Job runs once at 2am
10-server fleet:    ❌ Job runs 10 times at 2am — duplicate emails, double charges!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Solutions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Dedicated Scheduler Node
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;One designated machine runs all cron jobs&lt;/li&gt;
&lt;li&gt;✅ Simple&lt;/li&gt;
&lt;li&gt;❌ Single point of failure — if this node dies, no cron jobs run&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Leader Election + Cron
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Cluster elects a leader (using etcd/ZooKeeper)&lt;/li&gt;
&lt;li&gt;Only the leader runs cron jobs&lt;/li&gt;
&lt;li&gt;If leader dies → new leader elected → cron resumes&lt;/li&gt;
&lt;li&gt;✅ No SPOF | ❌ Leader election complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Distributed Lock Before Job Execution
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_job&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cron:daily-email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;locked&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 5min TTL
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;lock&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;# another node is running it
&lt;/span&gt;    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;send_daily_emails&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cron:daily-email&lt;/span&gt;&lt;span class="sh"&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;✅ Simple, works with existing Redis&lt;/li&gt;
&lt;li&gt;❌ Lock expiry must be &amp;gt; job duration, or use heartbeat extension&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Dedicated Scheduling Services
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS EventBridge Scheduler&lt;/strong&gt; — cron without servers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Temporal.io&lt;/strong&gt; — workflow orchestration with scheduling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Airflow&lt;/strong&gt; — DAG-based job scheduling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sidekiq-Cron&lt;/strong&gt; (Ruby), &lt;strong&gt;Bull&lt;/strong&gt; (Node.js)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Exactly-Once Execution
&lt;/h2&gt;

&lt;p&gt;For critical jobs (billing, emails):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use idempotency keys in the job itself&lt;/li&gt;
&lt;li&gt;Record execution in DB before running: &lt;code&gt;INSERT INTO job_runs (job_id, run_date) ON CONFLICT DO NOTHING&lt;/code&gt; → if 0 rows inserted, skip&lt;/li&gt;
&lt;li&gt;Use a distributed lock with heartbeat&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Observability for Cron
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Alert if a job DIDN'T run (missed heartbeat)&lt;/li&gt;
&lt;li&gt;Track job duration — sudden increase = something is wrong&lt;/li&gt;
&lt;li&gt;Log job result (success/failure, records processed)&lt;/li&gt;
&lt;li&gt;Tool: &lt;strong&gt;Healthchecks.io&lt;/strong&gt; — cron monitoring via ping&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>systemdesign</category>
      <category>distributedsystems</category>
      <category>backend</category>
      <category>api</category>
    </item>
    <item>
      <title>Cache Invalidation Patterns</title>
      <dc:creator>Gouranga Das Samrat</dc:creator>
      <pubDate>Sat, 17 Oct 2026 04:00:00 +0000</pubDate>
      <link>https://dev.to/gouranga-das-khulna/cache-invalidation-patterns-5bi6</link>
      <guid>https://dev.to/gouranga-das-khulna/cache-invalidation-patterns-5bi6</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;"There are only two hard things in computer science: cache invalidation and naming things." — Phil Karlton&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Core Problem
&lt;/h2&gt;

&lt;p&gt;Cache = stale data risk. Every caching strategy is a trade-off between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Freshness&lt;/strong&gt; — how up-to-date is cached data?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt; — how often do we go to the DB?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt; — does every user see the same thing?&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Strategy 1: TTL (Time-To-Live) Expiry
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user:123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# expires in 5 minutes
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Simplest strategy — let cache entries age out&lt;/li&gt;
&lt;li&gt;✅ No invalidation logic needed&lt;/li&gt;
&lt;li&gt;❌ Stale window = TTL duration (user sees old data for up to 5 min)&lt;/li&gt;
&lt;li&gt;❌ Cache stampede on expiry (see below)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choose TTL by data freshness requirement:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User profile: 5-15 minutes&lt;/li&gt;
&lt;li&gt;Product price: 1-5 minutes&lt;/li&gt;
&lt;li&gt;News article: 1 hour&lt;/li&gt;
&lt;li&gt;Static config: 1 day&lt;/li&gt;
&lt;li&gt;Currency rates: 1 minute&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Strategy 2: Write-Through Cache
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application writes → Cache AND DB simultaneously
Read → always hits cache (always warm)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;✅ Cache always up-to-date (no stale reads)&lt;/li&gt;
&lt;li&gt;✅ Reads always fast&lt;/li&gt;
&lt;li&gt;❌ Write latency doubles (must write both)&lt;/li&gt;
&lt;li&gt;❌ Cache may fill with data never read (write-heavy workloads waste memory)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Read-heavy workloads where writes are infrequent&lt;/p&gt;




&lt;h2&gt;
  
  
  Strategy 3: Write-Behind (Write-Back) Cache
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application writes → Cache only (async)
Background worker → flushes cache to DB periodically
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;✅ Very fast writes (no DB in critical path)&lt;/li&gt;
&lt;li&gt;❌ Data loss risk if cache crashes before flush&lt;/li&gt;
&lt;li&gt;❌ Complexity — need background flusher, failure handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; High-write scenarios where some data loss is acceptable (analytics, counters)&lt;/p&gt;




&lt;h2&gt;
  
  
  Strategy 4: Cache-Aside (Lazy Loading)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read request:
  Check cache → HIT: return ✅
              → MISS: query DB → store in cache → return

Write request:
  Write to DB
  Invalidate (delete) cache entry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Most common pattern&lt;/li&gt;
&lt;li&gt;✅ Cache only contains data that's actually requested&lt;/li&gt;
&lt;li&gt;✅ Cache failure doesn't affect writes&lt;/li&gt;
&lt;li&gt;❌ Cache miss = extra DB round trip (cold start penalty)&lt;/li&gt;
&lt;li&gt;❌ Race condition: two reads can populate stale data simultaneously&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Race condition fix:&lt;/strong&gt; Use a short TTL even when invalidating, or use versioned keys.&lt;/p&gt;




&lt;h2&gt;
  
  
  Strategy 5: Event-Driven Invalidation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DB changes → emit event (via Kafka, SNS, CDC) → cache service → delete/update cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;✅ Cache invalidated immediately on change&lt;/li&gt;
&lt;li&gt;✅ Decoupled — cache logic separate from write logic&lt;/li&gt;
&lt;li&gt;❌ Event delay = brief stale window&lt;/li&gt;
&lt;li&gt;❌ Event loss = permanently stale cache (need retry/DLQ)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;CDC (Change Data Capture):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Debezium reads Postgres WAL → emits change events → Kafka → cache invalidation service&lt;/li&gt;
&lt;li&gt;Low-latency, no application code change required&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚡ Cache Stampede (Thundering Herd)
&lt;/h2&gt;

&lt;p&gt;When a popular cache key expires → thousands of concurrent requests all hit DB simultaneously → DB overloaded → slow responses → everyone waits → chaos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solutions:&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Mutex/Lock on Cache Miss
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&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;data&lt;/span&gt;

    &lt;span class="c1"&gt;# Acquire lock — only one thread fetches from DB
&lt;/span&gt;    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lock:user:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# double-check after lock
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&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;data&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300&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;data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Early Expiry / Probabilistic Refresh
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Refresh cache before it actually expires&lt;/li&gt;
&lt;li&gt;Background thread proactively refreshes popular keys at ~80% of TTL&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Stale-While-Revalidate
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Serve stale data immediately&lt;/li&gt;
&lt;li&gt;Async: fetch fresh data in background, update cache&lt;/li&gt;
&lt;li&gt;Users see slightly stale but fast response&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Strategy 6: Versioned Cache Keys
&lt;/h2&gt;

&lt;p&gt;Instead of invalidating → use new key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# v1 data at key: user:123:v1&lt;/span&gt;
&lt;span class="c"&gt;# After update → write to user:123:v2&lt;/span&gt;
&lt;span class="c"&gt;# Old key auto-expires, new key is fresh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;✅ No invalidation race conditions&lt;/li&gt;
&lt;li&gt;✅ Old and new data can coexist during rollout&lt;/li&gt;
&lt;li&gt;❌ Old keys accumulate (need GC / short TTL on old versions)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚖️ Summary — Which Pattern When?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TTL&lt;/td&gt;
&lt;td&gt;Simple cases, infrequent updates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write-Through&lt;/td&gt;
&lt;td&gt;Read-heavy, must-be-fresh&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write-Behind&lt;/td&gt;
&lt;td&gt;High write throughput, some loss OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache-Aside&lt;/td&gt;
&lt;td&gt;General purpose ← &lt;strong&gt;default choice&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event-driven&lt;/td&gt;
&lt;td&gt;Complex systems, CDC, microservices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Versioned keys&lt;/td&gt;
&lt;td&gt;Zero-downtime cache updates&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Default interview answer:&lt;/strong&gt; Cache-aside + TTL + event-driven invalidation for critical data.&lt;/p&gt;




</description>
      <category>api</category>
      <category>backend</category>
      <category>systemdesign</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>API Design — Advanced</title>
      <dc:creator>Gouranga Das Samrat</dc:creator>
      <pubDate>Sun, 11 Oct 2026 04:00:00 +0000</pubDate>
      <link>https://dev.to/gouranga-das-khulna/api-design-advanced-86f</link>
      <guid>https://dev.to/gouranga-das-khulna/api-design-advanced-86f</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;You know the basics. Here's what separates good API design from great API design.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  📄 Pagination Strategies
&lt;/h2&gt;

&lt;p&gt;Never return unbounded lists. Always paginate.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Offset-Based Pagination
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /posts?limit=20&amp;amp;offset=40
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Simple, easy to implement&lt;/li&gt;
&lt;li&gt;✅ Jump to any page directly ("go to page 5")&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Page drift:&lt;/strong&gt; if items are added/deleted between requests, you miss or duplicate items&lt;/li&gt;
&lt;li&gt;❌ Performance degrades at high offsets (&lt;code&gt;OFFSET 100000&lt;/code&gt; scans 100k rows and discards them)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use when:&lt;/strong&gt; small datasets, admin panels, search results&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Cursor-Based Pagination (Recommended for feeds)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /posts?limit=20&amp;amp;cursor=eyJpZCI6MTIzfQ==
Response: { data: [...], nextCursor: "eyJpZCI6MTQ0fQ==" }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Cursor = encoded pointer to last item seen (e.g., base64 of &lt;code&gt;{id: 123}&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;✅ Stable — no page drift even if new items inserted&lt;/li&gt;
&lt;li&gt;✅ Efficient — WHERE id &amp;gt; 123 LIMIT 20 uses index perfectly&lt;/li&gt;
&lt;li&gt;❌ Can't jump to arbitrary pages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use when:&lt;/strong&gt; infinite scroll, Twitter/Instagram feeds, real-time data&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Keyset Pagination
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Variant of cursor — uses actual column values (not encoded)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /posts?limit=20&amp;amp;after_id=123&amp;amp;after_created_at=2024-01-15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Multi-column sort support&lt;/li&gt;
&lt;li&gt;Works naturally with DB indexes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Page-Based (Simple)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /posts?page=3&amp;amp;per_page=20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Like offset but friendlier naming&lt;/li&gt;
&lt;li&gt;Same problems as offset&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔍 Filtering &amp;amp; Sorting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Filtering Patterns
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;# Simple equality
GET /users?status=active&amp;amp;role=admin

# Range filters
GET /orders?created_after=2024-01-01&amp;amp;created_before=2024-12-31
GET /products?price_min=10&amp;amp;price_max=100

# Multi-value
GET /products?category=electronics,phones

# Search
GET /users?search=john
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;DB implications:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each filter = potential WHERE clause&lt;/li&gt;
&lt;li&gt;Ensure composite indexes match common filter combinations&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;status + role&lt;/code&gt; both filtered → composite index &lt;code&gt;(status, role)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Sorting
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /products?sort=price&amp;amp;order=asc
GET /products?sort=-created_at          # prefix - for desc
GET /products?sort=price,-created_at    # multi-sort
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Sort key must be indexed (or full table scan)&lt;/li&gt;
&lt;li&gt;For cursor pagination: sort field must be part of the cursor&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📊 GraphQL Basics
&lt;/h2&gt;

&lt;p&gt;REST: client gets what the server decides to send.&lt;br&gt;
GraphQL: client asks for exactly what it needs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="c"&gt;# REST: GET /users/123 returns ALL user fields&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# GraphQL: client specifies exactly what it wants&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;createdAt&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Over-fetching:&lt;/strong&gt; REST returns 50 fields, you need 3&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Under-fetching:&lt;/strong&gt; Need to call 3 REST endpoints to build one page&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type safety:&lt;/strong&gt; Schema defines exactly what's available&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to use GraphQL:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple clients (mobile, web, third-party) with different data needs&lt;/li&gt;
&lt;li&gt;Rapidly evolving APIs (add fields without breaking clients)&lt;/li&gt;
&lt;li&gt;Complex, interconnected data (think: social graph)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to stick with REST:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple CRUD APIs&lt;/li&gt;
&lt;li&gt;Public APIs (REST is more universally understood)&lt;/li&gt;
&lt;li&gt;File uploads, streaming (REST handles these better)&lt;/li&gt;
&lt;li&gt;Small team — GraphQL has operational overhead&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;N+1 Problem:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# Without DataLoader: 1 + N + N*M queries 🔥&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# With DataLoader: batch loads → 3 queries total ✅&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🏷️ API Versioning
&lt;/h2&gt;

&lt;p&gt;You mentioned this — here's the full picture:&lt;/p&gt;

&lt;h3&gt;
  
  
  URL Versioning (Most common)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /v1/users
GET /v2/users
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;✅ Very explicit, easy to route&lt;/li&gt;
&lt;li&gt;❌ URL "should" be a resource, not a version&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Header Versioning
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /users
Accept: application/vnd.myapi.v2+json
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;✅ Clean URLs&lt;/li&gt;
&lt;li&gt;❌ Harder to test (can't just paste URL in browser)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Query Parameter
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /users?version=2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;✅ Easy to test&lt;/li&gt;
&lt;li&gt;❌ Optional params = easy to forget → inconsistent behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Sunset Policy
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Announce deprecation date for old versions&lt;/li&gt;
&lt;li&gt;Return &lt;code&gt;Deprecation&lt;/code&gt; and &lt;code&gt;Sunset&lt;/code&gt; headers on old endpoints&lt;/li&gt;
&lt;li&gt;Give clients 6–12 months to migrate&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔒 API Best Practices (Interview Checklist)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Topic&lt;/th&gt;
&lt;th&gt;Best Practice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Auth&lt;/td&gt;
&lt;td&gt;Bearer token in &lt;code&gt;Authorization&lt;/code&gt; header, not URL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Errors&lt;/td&gt;
&lt;td&gt;Consistent error format: &lt;code&gt;{ error: { code, message, details } }&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate Limiting&lt;/td&gt;
&lt;td&gt;Return &lt;code&gt;Retry-After&lt;/code&gt; header, 429 status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Idempotency&lt;/td&gt;
&lt;td&gt;POST with &lt;code&gt;Idempotency-Key&lt;/code&gt; header for safe retries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Status Codes&lt;/td&gt;
&lt;td&gt;Use them correctly: 201 Created, 204 No Content, 409 Conflict&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HATEOAS&lt;/td&gt;
&lt;td&gt;Return links to related resources (optional but impressive)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compression&lt;/td&gt;
&lt;td&gt;gzip responses &amp;gt; 1KB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ⚖️ Trade-offs
&lt;/h2&gt;

&lt;p&gt;⚖️ &lt;strong&gt;REST vs GraphQL at interview:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Default to REST for simplicity&lt;/li&gt;
&lt;li&gt;Propose GraphQL when: multiple client types, complex data graph, bandwidth-sensitive (mobile)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⚖️ &lt;strong&gt;Offset vs Cursor pagination:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Default to cursor for production feeds&lt;/li&gt;
&lt;li&gt;Use offset for admin/search (random access needed)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>systemdesign</category>
      <category>distributedsystems</category>
      <category>api</category>
      <category>backend</category>
    </item>
  </channel>
</rss>
