<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Cryo Learn</title>
    <link>https://cryo-networks.com/en/</link>
    <description>Cryo Learn publishes clear, hands-on programming tutorials on Python, JavaScript, Git, SQL, Linux, CSS and algorithms, written for learners who want to truly understand their code.</description>
    <language>en</language>
    <lastBuildDate>Thu, 29 Oct 2026 10:07:21 +0000</lastBuildDate>
    <atom:link href="https://cryo-networks.com/en/feed.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>Writing Bash Scripts That Don&#39;t Break</title>
      <link>https://cryo-networks.com/en/posts/bash-scripts-that-dont-break/</link>
      <guid isPermaLink="true">https://cryo-networks.com/en/posts/bash-scripts-that-dont-break/</guid>
      <pubDate>Thu, 29 Oct 2026 09:00:00 +0000</pubDate>
      <category>Linux</category>
      <description>Most shell scripts work fine until a file name has a space in it or a command fails halfway through. Learn strict mode, quoting, safe temporary files and cleanup traps, then put it all together in a real backup script.</description>
      <content:encoded>&lt;p&gt;Bash scripts tend to start as a few commands pasted into a file. They work on your machine, with your files, on a good day. Then one evening a file name contains a space, a disk fills up, or a command fails halfway through, and the script keeps going as if nothing happened, sometimes deleting the wrong things on the way.&lt;/p&gt;
&lt;p&gt;The fixes are few and mechanical. This article goes through the habits that make scripts fail &lt;strong&gt;loudly and safely&lt;/strong&gt;, then combines them in a backup script you can actually use. It assumes you&#39;re comfortable with the commands in &lt;a href=&#34;/en/posts/linux-command-line-essentials/&#34;&gt;Essential Linux Commands Every Developer Should Know&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;start-every-script-the-same-way&#34;&gt;Start every script the same way&lt;/h2&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;ch&#34;&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-euo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pipefail&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;The first line (the &lt;em&gt;shebang&lt;/em&gt;) says which program runs the file. &lt;code&gt;/usr/bin/env bash&lt;/code&gt; finds &lt;code&gt;bash&lt;/code&gt; wherever it&#39;s installed, which matters on macOS and BSD. Make the file executable once with &lt;code&gt;chmod +x script.sh&lt;/code&gt; and run it as &lt;code&gt;./script.sh&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The second line turns on what&#39;s often called &lt;strong&gt;strict mode&lt;/strong&gt;. Each letter fixes a different silent failure.&lt;/p&gt;
&lt;figure class=&#34;diagram&#34;&gt;&lt;figcaption&gt;What strict mode catches: set -e stops after a failed cd, set -u turns a misspelled variable into an error, and pipefail makes a pipeline fail when any command in it fails&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&#34;set-e-stop-on-the-first-error&#34;&gt;set -e: stop on the first error&lt;/h2&gt;
&lt;p&gt;By default Bash ignores failures and runs the next line anyway:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;/var/www/myapp-typo&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# fails: No such file or directory&lt;/span&gt;
rm&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-rf&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;./*&lt;span class=&#34;w&#34;&gt;                    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# ...and now runs in whatever directory you were in&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;With &lt;code&gt;set -e&lt;/code&gt;, the script exits as soon as a command fails (returns a non-zero exit code). Failures that are expected, such as a &lt;code&gt;grep&lt;/code&gt; that finds nothing, can be handled explicitly:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;grep&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-q&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;ERROR&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;app.log&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Errors found&amp;quot;&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;

grep&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-q&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;ERROR&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;app.log&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;No errors&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# || also counts as handling it&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;admonition warning&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;Where set -e doesn&#39;t help&lt;/p&gt;
&lt;p&gt;&lt;code&gt;set -e&lt;/code&gt; is ignored in conditions (&lt;code&gt;if&lt;/code&gt;, &lt;code&gt;while&lt;/code&gt;, &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;, &lt;code&gt;||&lt;/code&gt;), and it has two well-known blind spots:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;local out=$(some_command)&lt;/code&gt; never fails, because the exit code you get is the one from &lt;code&gt;local&lt;/code&gt;. Declare and assign on separate lines: &lt;code&gt;local out&lt;/code&gt; then &lt;code&gt;out=$(some_command)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;((count++))&lt;/code&gt; returns 1 when &lt;code&gt;count&lt;/code&gt; was 0, which &lt;strong&gt;kills the script&lt;/strong&gt; under &lt;code&gt;set -e&lt;/code&gt;. Use &lt;code&gt;count=$((count + 1))&lt;/code&gt; instead.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h2 id=&#34;set-u-no-undefined-variables&#34;&gt;set -u: no undefined variables&lt;/h2&gt;
&lt;p&gt;A typo in a variable name normally expands to an empty string, silently:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Ada&amp;quot;&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Hello, &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$nme&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# prints &amp;quot;Hello, &amp;quot; without set -u&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;That&#39;s annoying in an &lt;code&gt;echo&lt;/code&gt;, and dangerous in &lt;code&gt;rm -rf &#34;$build_dir/&#34;*&lt;/code&gt;. With &lt;code&gt;set -u&lt;/code&gt;, using an unset variable is an error:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;./greet.sh: line 3: nme: unbound variable&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;For variables that are &lt;em&gt;allowed&lt;/em&gt; to be missing, give a default with &lt;code&gt;${var:-default}&lt;/code&gt;, or fail with your own message with &lt;code&gt;${var:?message}&lt;/code&gt;:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;nv&#34;&gt;port&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;PORT&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;:-&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;8080&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;                          &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# use $PORT if set, otherwise 8080&lt;/span&gt;
:&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;API_KEY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:?API_KEY must be set&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;         &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# stop with this error if it&amp;#39;s missing&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;set-o-pipefail-failures-inside-pipelines&#34;&gt;set -o pipefail: failures inside pipelines&lt;/h2&gt;
&lt;p&gt;A pipeline&#39;s exit code is normally that of its &lt;strong&gt;last&lt;/strong&gt; command, so failures earlier in the pipe vanish:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;true&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$?&lt;/span&gt;
&lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-o&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pipefail
$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;true&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$?&lt;/span&gt;
&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Without it, &lt;code&gt;mysqldump mydb | gzip &amp;gt; backup.sql.gz&lt;/code&gt; &#34;succeeds&#34; even when the dump fails, because &lt;code&gt;gzip&lt;/code&gt; happily compresses an empty input. With &lt;code&gt;pipefail&lt;/code&gt;, the pipeline fails if &lt;strong&gt;any&lt;/strong&gt; command in it fails.&lt;/p&gt;
&lt;h2 id=&#34;quote-your-variables&#34;&gt;Quote your variables&lt;/h2&gt;
&lt;p&gt;This is the single biggest source of shell bugs. An unquoted &lt;code&gt;$var&lt;/code&gt; is &lt;strong&gt;split on spaces&lt;/strong&gt; and then &lt;strong&gt;expanded as a wildcard pattern&lt;/strong&gt;:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;nv&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;my report.txt&amp;quot;&lt;/span&gt;
rm&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# rm: cannot remove &amp;#39;my&amp;#39;: No such file or directory&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;                  &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# rm: cannot remove &amp;#39;report.txt&amp;#39;: No such file or directory&lt;/span&gt;
rm&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# removes &amp;quot;my report.txt&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;And if a variable contains &lt;code&gt;*&lt;/code&gt;, an unquoted expansion turns it into a list of files in the current directory. The rule is simple: &lt;strong&gt;always put double quotes around variables and command substitutions&lt;/strong&gt;: &lt;code&gt;&#34;$file&#34;&lt;/code&gt;, &lt;code&gt;&#34;$(date +%F)&#34;&lt;/code&gt;, &lt;code&gt;&#34;${files[@]}&#34;&lt;/code&gt;. The rare exceptions are deliberate, and deserve a comment.&lt;/p&gt;
&lt;p&gt;To pass all of a script&#39;s arguments on to another command, use &lt;code&gt;&#34;$@&#34;&lt;/code&gt;. It keeps each argument intact, even when it contains spaces; &lt;code&gt;$*&lt;/code&gt; and unquoted &lt;code&gt;$@&lt;/code&gt; don&#39;t:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;c1&#34;&gt;# called as: ./wrapper.sh &amp;quot;a b&amp;quot; c&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;arg&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;in&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$@&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$arg&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;]&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# [a b] [c]&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;arg&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;in&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$arg&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;]&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# [a] [b] [c]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;admonition tip&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;Use [[ ]] for tests&lt;/p&gt;
&lt;p&gt;In Bash, prefer &lt;code&gt;[[ -f &#34;$file&#34; ]]&lt;/code&gt; to &lt;code&gt;[ -f &#34;$file&#34; ]&lt;/code&gt;. It doesn&#39;t split words, so a missing quote can&#39;t break it, and it supports &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;, &lt;code&gt;||&lt;/code&gt; and pattern matching like &lt;code&gt;[[ $name == *.txt ]]&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;read-files-line-by-line-correctly&#34;&gt;Read files line by line correctly&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;for line in $(cat file)&lt;/code&gt; loops over &lt;strong&gt;words&lt;/strong&gt;, not lines, and expands wildcards on the way. The correct idiom is:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;while&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;IFS&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;read&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-r&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;line&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Processing: &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$line&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;lt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;hosts.txt&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;IFS=&lt;/code&gt; keeps leading and trailing spaces, and &lt;code&gt;-r&lt;/code&gt; stops backslashes from being treated as escapes.&lt;/p&gt;
&lt;h2 id=&#34;clean-up-with-trap&#34;&gt;Clean up with trap&lt;/h2&gt;
&lt;p&gt;Scripts often create temporary files. &lt;code&gt;mktemp&lt;/code&gt; creates one with a unique, unguessable name (never hard-code &lt;code&gt;/tmp/myscript.tmp&lt;/code&gt;), and &lt;code&gt;trap ... EXIT&lt;/code&gt; guarantees it&#39;s removed however the script ends: normally, because of an error under &lt;code&gt;set -e&lt;/code&gt;, or when you press &lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;C&lt;/kbd&gt;:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;nv&#34;&gt;tmpdir&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;mktemp&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-d&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;trap&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;rm -rf &amp;quot;$tmpdir&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;EXIT

curl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-fsSL&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;https://example.com/data.csv&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-o&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tmpdir&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/data.csv&amp;quot;&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;# ...work with the file; no matter what happens, $tmpdir is deleted at the end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Note the single quotes: &lt;code&gt;$tmpdir&lt;/code&gt; is expanded when the trap &lt;em&gt;runs&lt;/em&gt;, not when it&#39;s defined. Only &lt;code&gt;kill -9&lt;/code&gt; (SIGKILL) skips the trap, because nothing can catch that signal.&lt;/p&gt;
&lt;h2 id=&#34;functions-arguments-and-errors&#34;&gt;Functions, arguments and errors&lt;/h2&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;die&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Error: &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$*&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;gt;&lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# error messages go to stderr&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;

require_dir&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;local&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;dir&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;              &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# local: don&amp;#39;t leak into the rest of the script&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[[&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-d&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$dir&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;die&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$dir&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#39; is not a directory&amp;quot;&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Inside a function, &lt;code&gt;$1&lt;/code&gt;, &lt;code&gt;$2&lt;/code&gt;… and &lt;code&gt;&#34;$@&#34;&lt;/code&gt; are the function&#39;s own arguments. Use &lt;code&gt;local&lt;/code&gt; for every variable a function creates, print errors to standard error (&lt;code&gt;&amp;gt;&amp;amp;2&lt;/code&gt;) so they don&#39;t mix with real output, and exit with a non-zero code so the caller (a person, &lt;code&gt;cron&lt;/code&gt; or CI) knows something went wrong.&lt;/p&gt;
&lt;h2 id=&#34;a-complete-example-a-backup-script&#34;&gt;A complete example: a backup script&lt;/h2&gt;
&lt;p&gt;Here&#39;s a script that archives a directory, keeps only the newest few archives, and puts everything above into practice:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;ch&#34;&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;# backup.sh: archive a directory and keep only the newest few archives.&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-euo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pipefail

usage&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Usage: &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$0&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; SOURCE_DIR DEST_DIR [KEEP]&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;gt;&lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;o&#34;&gt;[[&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$#&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-ge&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;usage
&lt;span class=&#34;nv&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;
&lt;span class=&#34;nv&#34;&gt;dest&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$2&lt;/span&gt;
&lt;span class=&#34;nv&#34;&gt;keep&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;:-&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;o&#34;&gt;[[&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-d&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$src&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Error: &amp;#39;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$src&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#39; is not a directory&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;gt;&lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
mkdir&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-p&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$dest&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;

&lt;span class=&#34;c1&#34;&gt;# Write to a temporary file first, so a failed run never leaves a half-written archive behind&lt;/span&gt;
&lt;span class=&#34;nv&#34;&gt;tmpfile&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;mktemp&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$dest&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/.backup.XXXXXX&amp;quot;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;trap&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;rm -f &amp;quot;$tmpfile&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;EXIT

&lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;basename&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$src&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;+%Y%m%d-%H%M%S&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;.tar.gz&amp;quot;&lt;/span&gt;
tar&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-czf&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tmpfile&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-C&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;dirname&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$src&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;basename&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$src&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;
mv&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tmpfile&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$dest&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Created &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$dest&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;

&lt;span class=&#34;c1&#34;&gt;# The timestamp in the name sorts alphabetically, so the glob lists the oldest first&lt;/span&gt;
&lt;span class=&#34;nv&#34;&gt;archives&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$dest&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;basename&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$src&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;-*.tar.gz&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;nv&#34;&gt;excess&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$((&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${#&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;archives&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[@]&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;keep&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;))&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;i&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;lt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;excess&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;i++&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;rm&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;archives&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[i]&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Removed &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;archives&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[i]&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;./backup.sh&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;my site&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;backups&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
Created&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;backups/my&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;site-20261029-081312.tar.gz
$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;./backup.sh&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;my site&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;backups&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
Created&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;backups/my&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;site-20261029-081313.tar.gz
$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;./backup.sh&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;my site&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;backups&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
Created&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;backups/my&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;site-20261029-081314.tar.gz
Removed&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;backups/my&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;site-20261029-081312.tar.gz&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;A few details worth noticing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It handles a source directory with a space in its name, because every variable is quoted.&lt;/li&gt;
&lt;li&gt;The archive is written under a temporary name and &lt;strong&gt;renamed&lt;/strong&gt; at the end. Renaming within one directory is atomic, so the backups folder never contains a half-written archive, even if &lt;code&gt;tar&lt;/code&gt; fails or the disk fills up. (&lt;code&gt;mktemp&lt;/code&gt; also makes the file readable only by you, which suits a backup.)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rm --&lt;/code&gt; marks the end of options, so a file name that starts with &lt;code&gt;-&lt;/code&gt; can&#39;t be mistaken for one.&lt;/li&gt;
&lt;li&gt;Wrong usage exits with code 2 and real errors with code 1, so a &lt;code&gt;cron&lt;/code&gt; job or CI pipeline can tell them apart.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;let-shellcheck-review-your-scripts&#34;&gt;Let ShellCheck review your scripts&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://www.shellcheck.net/&#34;&gt;ShellCheck&lt;/a&gt; is a linter for shell scripts. It catches missing quotes, the &lt;code&gt;local&lt;/code&gt; trap, &lt;code&gt;$*&lt;/code&gt; vs &lt;code&gt;&#34;$@&#34;&lt;/code&gt; and dozens of other mistakes, each with an explanation. Install it with &lt;code&gt;sudo apt install shellcheck&lt;/code&gt; (or &lt;code&gt;brew install shellcheck&lt;/code&gt;), run &lt;code&gt;shellcheck backup.sh&lt;/code&gt;, and add it to your editor or CI. It&#39;s the fastest way to absorb all of these habits.&lt;/p&gt;
&lt;h2 id=&#34;recap&#34;&gt;Recap&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Start with &lt;code&gt;#!/usr/bin/env bash&lt;/code&gt; and &lt;strong&gt;&lt;code&gt;set -euo pipefail&lt;/code&gt;&lt;/strong&gt;, and know where &lt;code&gt;set -e&lt;/code&gt; doesn&#39;t reach.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Quote every expansion&lt;/strong&gt;: &lt;code&gt;&#34;$var&#34;&lt;/code&gt;, &lt;code&gt;&#34;$(cmd)&#34;&lt;/code&gt;, &lt;code&gt;&#34;$@&#34;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;${var:-default}&lt;/code&gt; and &lt;code&gt;${var:?message}&lt;/code&gt; for optional and required values.&lt;/li&gt;
&lt;li&gt;Create temporary files with &lt;strong&gt;&lt;code&gt;mktemp&lt;/code&gt;&lt;/strong&gt; and remove them with &lt;strong&gt;&lt;code&gt;trap &#39;...&#39; EXIT&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Send errors to &lt;code&gt;&amp;gt;&amp;amp;2&lt;/code&gt;, exit with meaningful codes, and run &lt;strong&gt;ShellCheck&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;</content:encoded>
    </item>
    <item>
      <title>The JavaScript Event Loop: Tasks, Microtasks and Rendering</title>
      <link>https://cryo-networks.com/en/posts/javascript-event-loop/</link>
      <guid isPermaLink="true">https://cryo-networks.com/en/posts/javascript-event-loop/</guid>
      <pubDate>Thu, 29 Oct 2026 09:00:00 +0000</pubDate>
      <category>JavaScript</category>
      <description>Why does a resolved promise always beat setTimeout(fn, 0)? Learn exactly how the event loop schedules tasks, microtasks and repaints, and how to keep long work from freezing the page.</description>
      <content:encoded>&lt;p&gt;You already know that &lt;code&gt;await&lt;/code&gt; pauses a function without freezing the page. But &lt;em&gt;when&lt;/em&gt; exactly does that function resume? Why does a resolved promise always run before &lt;code&gt;setTimeout(fn, 0)&lt;/code&gt;? And why can a &#34;Loading…&#34; message you set right before a heavy calculation never appear on screen?&lt;/p&gt;
&lt;p&gt;All three questions have the same answer: the &lt;strong&gt;event loop&lt;/strong&gt;. Once you can run it in your head, a whole class of timing bugs, flaky tests and janky interfaces stops being mysterious. This article assumes you&#39;re comfortable with promises and &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;; if not, start with &lt;a href=&#34;/en/posts/javascript-async-await/&#34;&gt;Understanding async/await in JavaScript&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;the-pieces&#34;&gt;The pieces&lt;/h2&gt;
&lt;p&gt;JavaScript in the browser runs on one main thread, and that thread does more than run your code. It also handles input and paints the screen. Four things share it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The call stack&lt;/strong&gt;: the functions running right now. The event loop only steps in when it is empty.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The task queue&lt;/strong&gt;: whole units of work waiting their turn, such as a &lt;code&gt;setTimeout&lt;/code&gt; callback, a click handler, or a &lt;code&gt;postMessage&lt;/code&gt; from a worker. (Specs call these &lt;em&gt;tasks&lt;/em&gt;; you&#39;ll also see them called &lt;em&gt;macrotasks&lt;/em&gt;.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The microtask queue&lt;/strong&gt;: small follow-up jobs, mainly promise reactions (&lt;code&gt;.then()&lt;/code&gt; callbacks and code after an &lt;code&gt;await&lt;/code&gt;), plus &lt;code&gt;queueMicrotask()&lt;/code&gt; and &lt;code&gt;MutationObserver&lt;/code&gt; callbacks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The rendering steps&lt;/strong&gt;: &lt;code&gt;requestAnimationFrame&lt;/code&gt; callbacks, then style, layout and paint.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;one-turn-of-the-loop&#34;&gt;One turn of the loop&lt;/h2&gt;
&lt;p&gt;Stripped to its essentials, the browser repeats this forever:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;while&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;task&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;taskQueue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;takeOldest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;task&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;task&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;                     &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 1. exactly ONE task&lt;/span&gt;

&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;while&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;microtaskQueue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 2. EVERY microtask,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;microtaskQueue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;           &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;//    including ones added meanwhile&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;isTimeToRender&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;                  &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 3. maybe a frame&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;runAnimationFrameCallbacks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;updateStyleLayoutAndPaint&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;diagram&#34;&gt;&lt;figcaption&gt;Each turn of the event loop runs one task, then empties the microtask queue, then renders a frame if one is due&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;The asymmetry is the whole story: the loop takes &lt;strong&gt;one&lt;/strong&gt; task at a time, but drains the microtask queue &lt;strong&gt;completely&lt;/strong&gt;, even if microtasks keep adding more microtasks. Rendering only happens &lt;em&gt;between&lt;/em&gt; tasks, and only when the display is ready for a new frame (about every 16.7 ms on a 60 Hz screen).&lt;/p&gt;
&lt;div class=&#34;admonition note&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;Your script is a task too&lt;/p&gt;
&lt;p&gt;The initial run of a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; is itself a task. That&#39;s why promise callbacks created during it run as soon as the script finishes, before any timer.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;predict-the-output&#34;&gt;Predict the output&lt;/h2&gt;
&lt;p&gt;Here&#39;s a classic puzzle. Work out the order before reading the answer:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;script start&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

&lt;span class=&#34;nx&#34;&gt;setTimeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;timeout&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

&lt;span class=&#34;nb&#34;&gt;Promise&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;resolve&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;then 1&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;then 2&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;

&lt;span class=&#34;nx&#34;&gt;queueMicrotask&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;microtask&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;async&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;async start&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;await&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;async after await&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;})();&lt;/span&gt;

&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;script end&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;script start
async start
script end
then 1
microtask
async after await
then 2
timeout&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Step by step:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The synchronous code runs first: &lt;code&gt;script start&lt;/code&gt;, then &lt;code&gt;async start&lt;/code&gt; (an async function runs synchronously up to its first &lt;code&gt;await&lt;/code&gt;), then &lt;code&gt;script end&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;While that happened, three microtasks were queued in order: &lt;code&gt;then 1&lt;/code&gt;, &lt;code&gt;microtask&lt;/code&gt;, and the rest of the async function. The timer callback went to the &lt;strong&gt;task&lt;/strong&gt; queue.&lt;/li&gt;
&lt;li&gt;The script task ends, so the microtask queue is drained. Running &lt;code&gt;then 1&lt;/code&gt; resolves the next promise in the chain, which queues &lt;code&gt;then 2&lt;/code&gt; at the &lt;em&gt;back&lt;/em&gt; of the queue. That&#39;s why &lt;code&gt;then 2&lt;/code&gt; comes after &lt;code&gt;async after await&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Only when the microtask queue is empty does the loop pick the next task: the timer.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;setTimeout(fn, 0)&lt;/code&gt; means &#34;run &lt;code&gt;fn&lt;/code&gt; in a &lt;em&gt;future task&lt;/em&gt;&#34;, not &#34;run it now&#34;. Every microtask already queued, and every microtask &lt;em&gt;they&lt;/em&gt; queue, runs first.&lt;/p&gt;
&lt;h2 id=&#34;microtasks-can-starve-the-page&#34;&gt;Microtasks can starve the page&lt;/h2&gt;
&lt;p&gt;Because the loop won&#39;t leave step 2 until the microtask queue is empty, a microtask that keeps scheduling another one never lets the page render or respond:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;spinForever&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Promise&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;resolve&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;spinForever&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// freezes the tab: rendering never gets a turn&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Swap the promise for a timer and the page stays responsive, because every timer callback is a separate task and the loop can render between them:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;spinPolitely&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;setTimeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;spinPolitely&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;           &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// one task per call: the browser can paint in between&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;admonition warning&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;setTimeout(fn, 0) isn&#39;t really 0&lt;/p&gt;
&lt;p&gt;After timers have been nested five levels deep, browsers clamp the delay to at least 4 ms, and background tabs throttle timers much harder. Never use timers for precise timing.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;why-your-loading-message-never-shows&#34;&gt;Why your loading message never shows&lt;/h2&gt;
&lt;p&gt;This is the most common real-world event loop bug:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;nx&#34;&gt;button&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;addEventListener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;click&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;textContent&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Crunching numbers…&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;crunchNumbers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;                 &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// synchronous, takes ~2 seconds&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;textContent&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Done!&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Users never see &#34;Crunching numbers…&#34;. The DOM &lt;em&gt;was&lt;/em&gt; updated, but painting only happens between tasks, and the click handler is one long task. By the time the browser gets to paint, the text already says &#34;Done!&#34;. Meanwhile the page ignores every click and keystroke for two seconds.&lt;/p&gt;
&lt;p&gt;The fix is to break long work into chunks and &lt;strong&gt;yield&lt;/strong&gt; to the event loop between them. The yield must create a new &lt;em&gt;task&lt;/em&gt;: &lt;code&gt;await Promise.resolve()&lt;/code&gt; only queues a microtask, so it never lets the browser paint.&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;yieldToMain&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;globalThis&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;scheduler&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;yield&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;scheduler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;yield&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;                              &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// newer browsers&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;ow&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Promise&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;resolve&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;setTimeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;resolve&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// works everywhere&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;async&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;processAll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lastYield&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;performance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;now&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;

&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;item&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;of&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;processItem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;performance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;now&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lastYield&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;50&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// a &amp;quot;long task&amp;quot; is anything over 50 ms&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;await&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;yieldToMain&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;                      &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// let the browser paint and handle input&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lastYield&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;performance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;now&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Now the loading message appears, clicks are handled, and the work still finishes quickly because it only yields every 50 ms rather than after every item. &lt;code&gt;scheduler.yield()&lt;/code&gt; is the better choice where it exists: the browser gives your continuation priority over other waiting tasks, so other code can&#39;t keep jumping in ahead of your work.&lt;/p&gt;
&lt;div class=&#34;admonition tip&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;When chunking isn&#39;t enough&lt;/p&gt;
&lt;p&gt;If the work itself takes seconds of pure computation (parsing a huge file, image processing), move it to a &lt;strong&gt;Web Worker&lt;/strong&gt;. Workers have their own thread and their own event loop, so the main thread stays free.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;requestanimationframe-work-at-the-right-moment&#34;&gt;requestAnimationFrame: work at the right moment&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;requestAnimationFrame&lt;/code&gt; callbacks run in step 3, right before the browser paints. That makes them the right place for visual updates: you run exactly once per frame, never twice between paints and never in the middle of one.&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;animate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;box&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;style&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;transform&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`translateX(&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;%&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;600&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;px)`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;requestAnimationFrame&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;animate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// schedule the next frame&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;nx&#34;&gt;requestAnimationFrame&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;animate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Driving the same animation with &lt;code&gt;setTimeout(animate, 16)&lt;/code&gt; drifts out of sync with the display, so some frames get two updates and others none, which is exactly the stutter users notice.&lt;/p&gt;
&lt;h2 id=&#34;a-subtle-one-microtasks-between-event-listeners&#34;&gt;A subtle one: microtasks between event listeners&lt;/h2&gt;
&lt;p&gt;Microtasks run whenever the call stack becomes empty, not only at the end of a task. Look at two listeners on the same button:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;nx&#34;&gt;button&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;addEventListener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;click&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Promise&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;resolve&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;microtask 1&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;listener 1&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;

&lt;span class=&#34;nx&#34;&gt;button&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;addEventListener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;click&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Promise&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;resolve&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;microtask 2&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;listener 2&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Triggered by&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A real user click&lt;/td&gt;
&lt;td&gt;&lt;code&gt;listener 1&lt;/code&gt;, &lt;code&gt;microtask 1&lt;/code&gt;, &lt;code&gt;listener 2&lt;/code&gt;, &lt;code&gt;microtask 2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;button.click()&lt;/code&gt; in code&lt;/td&gt;
&lt;td&gt;&lt;code&gt;listener 1&lt;/code&gt;, &lt;code&gt;listener 2&lt;/code&gt;, &lt;code&gt;microtask 1&lt;/code&gt;, &lt;code&gt;microtask 2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;When a user clicks, the browser calls each listener with an empty stack, so microtasks run in between. When &lt;em&gt;your code&lt;/em&gt; calls &lt;code&gt;button.click()&lt;/code&gt;, your code is still on the stack, so every microtask waits until both listeners have finished. Keep this in mind when a test that simulates clicks behaves differently from the real page.&lt;/p&gt;
&lt;h2 id=&#34;nodejs-adds-two-extra-queues&#34;&gt;Node.js adds two extra queues&lt;/h2&gt;
&lt;p&gt;Node.js uses the same model (one task at a time, microtasks drained after each callback) but adds two scheduling tools of its own:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;nx&#34;&gt;setTimeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;timeout&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;setImmediate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;immediate&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;Promise&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;resolve&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;promise&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;process&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;nextTick&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;nextTick&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Run as a CommonJS script, this prints &lt;code&gt;nextTick&lt;/code&gt;, then &lt;code&gt;promise&lt;/code&gt;, then &lt;code&gt;timeout&lt;/code&gt; and &lt;code&gt;immediate&lt;/code&gt; in an order that can change from run to run.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;process.nextTick()&lt;/code&gt; callbacks run &lt;strong&gt;before&lt;/strong&gt; promise microtasks. Use it sparingly: recursive &lt;code&gt;nextTick&lt;/code&gt; calls starve the loop just like recursive microtasks.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setImmediate()&lt;/code&gt; runs in a dedicated phase after I/O callbacks. Inside an I/O callback it always fires before a &lt;code&gt;setTimeout(fn, 0)&lt;/code&gt;; from the main script the order isn&#39;t guaranteed.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;admonition note&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;ES modules&lt;/p&gt;
&lt;p&gt;In an ES module (&lt;code&gt;.mjs&lt;/code&gt;), &lt;code&gt;promise&lt;/code&gt; may print before &lt;code&gt;nextTick&lt;/code&gt;, because the module body itself is evaluated from inside the microtask queue. Don&#39;t write code that depends on the exact order of &lt;code&gt;nextTick&lt;/code&gt; and promises.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;recap&#34;&gt;Recap&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The event loop runs &lt;strong&gt;one task&lt;/strong&gt;, then &lt;strong&gt;all microtasks&lt;/strong&gt;, then &lt;strong&gt;renders&lt;/strong&gt; if a frame is due, and repeats.&lt;/li&gt;
&lt;li&gt;Promise callbacks, code after &lt;code&gt;await&lt;/code&gt; and &lt;code&gt;queueMicrotask()&lt;/code&gt; are &lt;strong&gt;microtasks&lt;/strong&gt;: they always run before the next timer, event or paint.&lt;/li&gt;
&lt;li&gt;A microtask that schedules microtasks forever &lt;strong&gt;freezes the page&lt;/strong&gt;; timers don&#39;t, because each one is a separate task.&lt;/li&gt;
&lt;li&gt;Long synchronous work blocks painting and input. Split it up and yield with &lt;code&gt;setTimeout&lt;/code&gt; or &lt;code&gt;scheduler.yield()&lt;/code&gt;. &lt;code&gt;await Promise.resolve()&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; yield.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;&lt;code&gt;requestAnimationFrame&lt;/code&gt;&lt;/strong&gt; for visual updates and &lt;strong&gt;Web Workers&lt;/strong&gt; for heavy computation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next time something runs &#34;in the wrong order&#34;, ask yourself: &lt;em&gt;is this a task or a microtask, and what else is in that queue?&lt;/em&gt;&lt;/p&gt;</content:encoded>
    </item>
    <item>
      <title>The CSS Cascade and Specificity: Why Your Styles Don&#39;t Apply</title>
      <link>https://cryo-networks.com/en/posts/css-cascade-specificity/</link>
      <guid isPermaLink="true">https://cryo-networks.com/en/posts/css-cascade-specificity/</guid>
      <pubDate>Thu, 29 Oct 2026 09:00:00 +0000</pubDate>
      <category>CSS</category>
      <description>When two CSS rules disagree, the browser follows a strict set of rules to pick a winner. Learn how the cascade decides, calculate specificity by hand, and stop reaching for !important.</description>
      <content:encoded>&lt;p&gt;You write a CSS rule. You reload. Nothing changes. You open DevTools and there it is, &lt;strong&gt;crossed out&lt;/strong&gt;: another rule somewhere is winning. So you add a class, then an ID, then &lt;code&gt;!important&lt;/code&gt;, and six months later nobody dares touch the stylesheet.&lt;/p&gt;
&lt;p&gt;There&#39;s nothing random about which rule wins. When several declarations set the same property on the same element, the browser runs the &lt;strong&gt;cascade&lt;/strong&gt;: a fixed sequence of tie-breakers. Learn the sequence and you can predict the winner every time, and write CSS that doesn&#39;t need &lt;code&gt;!important&lt;/code&gt; at all. (If you&#39;re still getting comfortable with layout, &lt;a href=&#34;/en/posts/css-flexbox-vs-grid/&#34;&gt;CSS Flexbox vs Grid&lt;/a&gt; is a good first read.)&lt;/p&gt;
&lt;h2 id=&#34;the-cascade-in-order&#34;&gt;The cascade, in order&lt;/h2&gt;
&lt;p&gt;For each property of each element, the browser collects every declaration that applies, then compares them in this order. &lt;strong&gt;The first step that finds a difference decides&lt;/strong&gt;; later steps only break ties.&lt;/p&gt;
&lt;figure class=&#34;diagram&#34;&gt;&lt;figcaption&gt;The cascade compares declarations step by step: origin and importance, inline style, cascade layer, specificity, then order of appearance; the first step with a difference picks the winner&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Origin and importance.&lt;/strong&gt; Your stylesheets beat the browser&#39;s default styles. A declaration marked &lt;code&gt;!important&lt;/code&gt; beats every normal one.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inline styles.&lt;/strong&gt; A &lt;code&gt;style=&#34;…&#34;&lt;/code&gt; attribute beats any selector in a stylesheet.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cascade layers.&lt;/strong&gt; If you use &lt;code&gt;@layer&lt;/code&gt;, styles in later layers beat earlier ones, whatever their selectors say.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Specificity.&lt;/strong&gt; The more specific selector wins.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Order of appearance.&lt;/strong&gt; If everything else is equal, the declaration that comes &lt;strong&gt;last&lt;/strong&gt; wins.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Most day-to-day surprises happen at steps 4 and 5, so let&#39;s start there.&lt;/p&gt;
&lt;h2 id=&#34;calculating-specificity&#34;&gt;Calculating specificity&lt;/h2&gt;
&lt;p&gt;Specificity is a score made of three numbers, usually written &lt;strong&gt;(A, B, C)&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A&lt;/strong&gt;: the number of &lt;strong&gt;ID&lt;/strong&gt; selectors (&lt;code&gt;#header&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;B&lt;/strong&gt;: the number of &lt;strong&gt;class&lt;/strong&gt; selectors (&lt;code&gt;.card&lt;/code&gt;), &lt;strong&gt;attribute&lt;/strong&gt; selectors (&lt;code&gt;[type=&#34;email&#34;]&lt;/code&gt;) and &lt;strong&gt;pseudo-classes&lt;/strong&gt; (&lt;code&gt;:hover&lt;/code&gt;, &lt;code&gt;:focus&lt;/code&gt;, &lt;code&gt;:nth-child()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;C&lt;/strong&gt;: the number of &lt;strong&gt;type&lt;/strong&gt; selectors (&lt;code&gt;p&lt;/code&gt;, &lt;code&gt;button&lt;/code&gt;) and &lt;strong&gt;pseudo-elements&lt;/strong&gt; (&lt;code&gt;::before&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The universal selector &lt;code&gt;*&lt;/code&gt; and combinators (&lt;code&gt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;~&lt;/code&gt;) count for nothing.&lt;/p&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Selector&lt;/th&gt;
&lt;th&gt;Specificity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(0, 0, 1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.card p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(0, 1, 1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.card .title&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(0, 2, 0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a:hover&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(0, 1, 1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;input[type=&#34;email&#34;]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(0, 1, 1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ul li::before&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(0, 0, 3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;#sidebar a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(1, 0, 1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;#nav .item a:hover&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(1, 2, 1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(0, 0, 0)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;To compare two selectors, compare &lt;strong&gt;A&lt;/strong&gt; first. Only if A is equal do you look at B, and only if B is equal do you look at C. There&#39;s no carrying over between columns: one ID (1, 0, 0) beats a selector with twenty classes (0, 20, 0).&lt;/p&gt;
&lt;p&gt;Here&#39;s a classic surprise:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;button&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;background&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;gray&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;/* (1, 1, 0) */&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;button&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;primary&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;background&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;blue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;/* (0, 2, 0) */&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;main&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;button&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;button primary&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Save&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;button&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;   &lt;span class=&#34;cm&#34;&gt;&amp;lt;!-- gray! --&amp;gt;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;The primary button is gray, even though &lt;code&gt;.button.primary&lt;/code&gt; looks more specific to a human and comes later. The ID wins at column A, and step 5 is never reached.&lt;/p&gt;
&lt;div class=&#34;admonition tip&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;Let DevTools do the maths&lt;/p&gt;
&lt;p&gt;In Chromium-based browsers, hover over a selector in the &lt;strong&gt;Styles&lt;/strong&gt; pane to see its specificity. The &lt;strong&gt;Computed&lt;/strong&gt; tab shows the final value of each property and, when you expand it, every rule that tried to set it, winner first.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;is-not-has-and-where&#34;&gt;:is(), :not(), :has() and :where()&lt;/h2&gt;
&lt;p&gt;These pseudo-classes take a list of selectors, and they count differently:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;:is()&lt;/code&gt;, &lt;code&gt;:not()&lt;/code&gt; and &lt;code&gt;:has()&lt;/code&gt; take the specificity of their &lt;strong&gt;most specific argument&lt;/strong&gt;. &lt;code&gt;:is(#nav, .menu) a&lt;/code&gt; is (1, 0, 1), even when it matches through &lt;code&gt;.menu&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:where()&lt;/code&gt; always has &lt;strong&gt;zero&lt;/strong&gt; specificity, whatever is inside it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That makes &lt;code&gt;:where()&lt;/code&gt; perfect for defaults that should be easy to override:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;c&#34;&gt;/* (0, 0, 1): any later class beats it */&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;where&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;prose&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;royalblue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;c&#34;&gt;/* (0, 1, 1): would beat most component styles, which is usually not what you want */&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;prose&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;royalblue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;inheritance-is-not-the-cascade&#34;&gt;Inheritance is not the cascade&lt;/h2&gt;
&lt;p&gt;Some properties, mostly text-related ones like &lt;code&gt;color&lt;/code&gt;, &lt;code&gt;font-family&lt;/code&gt; and &lt;code&gt;line-height&lt;/code&gt;, are &lt;strong&gt;inherited&lt;/strong&gt;: if nothing sets them on an element, it takes its parent&#39;s value. Others, like &lt;code&gt;margin&lt;/code&gt;, &lt;code&gt;border&lt;/code&gt; and &lt;code&gt;background&lt;/code&gt;, are not.&lt;/p&gt;
&lt;p&gt;The trap: &lt;strong&gt;an inherited value always loses to any rule that targets the element directly&lt;/strong&gt;, however weak that rule&#39;s selector is.&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;article&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;darkred&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;/* (1, 0, 0), but applies to the div */&lt;/span&gt;
&lt;span class=&#34;nt&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;black&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;/* (0, 0, 1), applies to the p itself */&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;article&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;This text is black, not dark red.&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Specificity only compares rules that match the &lt;strong&gt;same element&lt;/strong&gt;. The &lt;code&gt;#article&lt;/code&gt; rule matches the &lt;code&gt;div&lt;/code&gt;; the paragraph only inherits from it, and inheritance only kicks in when no rule sets &lt;code&gt;color&lt;/code&gt; on the paragraph itself.&lt;/p&gt;
&lt;p&gt;Four keywords let you control this explicitly on any property:&lt;/p&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Keyword&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;inherit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Take the parent&#39;s value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;initial&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use the property&#39;s default value from the CSS spec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unset&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;inherit&lt;/code&gt; for inherited properties, &lt;code&gt;initial&lt;/code&gt; for the others&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;revert&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Go back to what the browser&#39;s own stylesheet would set&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&#34;important-and-inline-styles&#34;&gt;!important and inline styles&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;!important&lt;/code&gt; jumps the queue at step 1, before specificity is even looked at. That&#39;s exactly why it causes trouble: the only way to override an &lt;code&gt;!important&lt;/code&gt; declaration is with another &lt;code&gt;!important&lt;/code&gt; declaration that wins on the later steps, and so the arms race begins.&lt;/p&gt;
&lt;p&gt;Inline &lt;code&gt;style&lt;/code&gt; attributes (step 2) beat any selector, but lose to &lt;code&gt;!important&lt;/code&gt; in a stylesheet.&lt;/p&gt;
&lt;div class=&#34;admonition warning&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;Keep !important for utilities&lt;/p&gt;
&lt;p&gt;A reasonable use is a small utility class that must always win, like &lt;code&gt;.hidden { display: none !important; }&lt;/code&gt;. Using it to fix a specificity problem only hides the problem until the next one.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;cascade-layers-specificity-under-control&#34;&gt;Cascade layers: specificity under control&lt;/h2&gt;
&lt;p&gt;Cascade layers (&lt;code&gt;@layer&lt;/code&gt;, supported in all major browsers since 2022) add a step &lt;strong&gt;above&lt;/strong&gt; specificity. You declare the order of your layers once, and from then on a later layer beats an earlier one, whatever the selectors:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;layer&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;reset&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;vendor&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;components&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;utilities&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;layer&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;vendor&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;btn&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;background&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;gray&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;/* (1, 1, 0) */&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;layer&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;components&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;btn&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;background&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;royalblue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;/* (0, 1, 0), and it wins */&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;components&lt;/code&gt; comes after &lt;code&gt;vendor&lt;/code&gt; in the &lt;code&gt;@layer&lt;/code&gt; statement, so its &lt;code&gt;.btn&lt;/code&gt; wins despite much lower specificity. This is ideal for third-party CSS: wrap it in an early layer (&lt;code&gt;@import url(&#34;library.css&#34;) layer(vendor);&lt;/code&gt;) and your own styles override it without specificity tricks.&lt;/p&gt;
&lt;p&gt;Two details worth knowing: styles that aren&#39;t in any layer beat all layered styles, and for &lt;code&gt;!important&lt;/code&gt; declarations the layer order is reversed, so an early layer&#39;s &lt;code&gt;!important&lt;/code&gt; beats a later one&#39;s.&lt;/p&gt;
&lt;h2 id=&#34;habits-that-prevent-specificity-wars&#34;&gt;Habits that prevent specificity wars&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Style with classes, not IDs.&lt;/strong&gt; Keep IDs for anchors and JavaScript.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep selectors short and flat.&lt;/strong&gt; &lt;code&gt;.card-title&lt;/code&gt; instead of &lt;code&gt;.sidebar .card &amp;gt; h2.title&lt;/code&gt;. Flat selectors all have similar specificity, so source order and layers decide, and those are easy to reason about.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wrap defaults and resets in &lt;code&gt;:where()&lt;/code&gt;&lt;/strong&gt; so they&#39;re trivial to override.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use &lt;code&gt;@layer&lt;/code&gt;&lt;/strong&gt; to put third-party and base styles below your components.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When a rule doesn&#39;t apply, check DevTools first&lt;/strong&gt;: is it crossed out (lost the cascade), missing (selector doesn&#39;t match), or shown with a warning (invalid value)?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;recap&#34;&gt;Recap&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The cascade picks a winner in a fixed order: &lt;strong&gt;origin and &lt;code&gt;!important&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;inline styles&lt;/strong&gt;, &lt;strong&gt;layers&lt;/strong&gt;, &lt;strong&gt;specificity&lt;/strong&gt;, then &lt;strong&gt;order of appearance&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Specificity is &lt;strong&gt;(IDs, classes/attributes/pseudo-classes, elements/pseudo-elements)&lt;/strong&gt;, compared column by column, with no carrying over.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:is()&lt;/code&gt;, &lt;code&gt;:not()&lt;/code&gt; and &lt;code&gt;:has()&lt;/code&gt; take their strongest argument&#39;s specificity; &lt;code&gt;:where()&lt;/code&gt; adds &lt;strong&gt;zero&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inherited values lose&lt;/strong&gt; to any rule that targets the element directly.&lt;/li&gt;
&lt;li&gt;Prefer &lt;strong&gt;flat class selectors&lt;/strong&gt;, &lt;code&gt;:where()&lt;/code&gt; and &lt;code&gt;@layer&lt;/code&gt; over IDs and &lt;code&gt;!important&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;</content:encoded>
    </item>
    <item>
      <title>SQL Window Functions: Running Totals, Rankings and Moving Averages</title>
      <link>https://cryo-networks.com/en/posts/sql-window-functions/</link>
      <guid isPermaLink="true">https://cryo-networks.com/en/posts/sql-window-functions/</guid>
      <pubDate>Thu, 29 Oct 2026 09:00:00 +0000</pubDate>
      <category>SQL</category>
      <description>GROUP BY squashes rows together; window functions calculate across rows and keep every one of them. Learn OVER, PARTITION BY, ROW_NUMBER, RANK, LAG and window frames with small, runnable examples.</description>
      <content:encoded>&lt;p&gt;Sooner or later a report asks for something &lt;code&gt;GROUP BY&lt;/code&gt; can&#39;t do. &lt;em&gt;Show each sale next to its region&#39;s total.&lt;/em&gt; &lt;em&gt;Rank sales reps within each region.&lt;/em&gt; &lt;em&gt;Show this month&#39;s revenue next to last month&#39;s.&lt;/em&gt; With &lt;code&gt;GROUP BY&lt;/code&gt; you get one row per group and lose the details; people often fall back to self-joins, subqueries or a loop in application code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Window functions&lt;/strong&gt; solve all of these in a single query. They compute values across a set of related rows (the &lt;em&gt;window&lt;/em&gt;) &lt;strong&gt;without collapsing them&lt;/strong&gt;: every input row is still in the output, with the calculated value added as a new column.&lt;/p&gt;
&lt;p&gt;They work in PostgreSQL, MySQL 8+, SQL Server, Oracle and SQLite 3.25+. The examples below run as-is in SQLite. If JOINs still feel shaky, start with &lt;a href=&#34;/en/posts/sql-joins-explained/&#34;&gt;SQL JOINs Explained with Simple Examples&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;our-example-data&#34;&gt;Our example data&lt;/h2&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;TABLE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sales&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rep&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;TEXT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;region&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;TEXT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;INTEGER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;INSERT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INTO&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sales&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;VALUES&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Ana&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;North&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;900&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Ben&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;North&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;700&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Cleo&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;North&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;700&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Dev&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;North&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;400&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Eve&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;South&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1200&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Finn&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;South&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;650&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Gus&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;South&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;300&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;TABLE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;monthly&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;month&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;TEXT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;revenue&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;INTEGER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;INSERT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INTO&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;monthly&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;VALUES&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;2026-01&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;12000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;2026-02&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;15000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;2026-03&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;11000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;2026-04&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;18000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;2026-05&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;21000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;2026-06&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;19500&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;group-by-vs-a-window&#34;&gt;GROUP BY vs a window&lt;/h2&gt;
&lt;p&gt;Here&#39;s the total per region with &lt;code&gt;GROUP BY&lt;/code&gt;. Seven rows go in, two come out:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;region&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SUM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;total&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sales&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;GROUP&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;region&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;region&lt;/th&gt;
&lt;th&gt;total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;North&lt;/td&gt;
&lt;td&gt;2700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;South&lt;/td&gt;
&lt;td&gt;2150&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Now the same &lt;code&gt;SUM&lt;/code&gt;, but followed by &lt;strong&gt;&lt;code&gt;OVER&lt;/code&gt;&lt;/strong&gt;, which turns it into a window function:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rep&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;region&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SUM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OVER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PARTITION&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;region&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;region_total&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ROUND&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SUM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OVER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PARTITION&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;region&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pct_of_region&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sales&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;region&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DESC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;rep&lt;/th&gt;
&lt;th&gt;region&lt;/th&gt;
&lt;th&gt;amount&lt;/th&gt;
&lt;th&gt;region_total&lt;/th&gt;
&lt;th&gt;pct_of_region&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ana&lt;/td&gt;
&lt;td&gt;North&lt;/td&gt;
&lt;td&gt;900&lt;/td&gt;
&lt;td&gt;2700&lt;/td&gt;
&lt;td&gt;33.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ben&lt;/td&gt;
&lt;td&gt;North&lt;/td&gt;
&lt;td&gt;700&lt;/td&gt;
&lt;td&gt;2700&lt;/td&gt;
&lt;td&gt;25.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cleo&lt;/td&gt;
&lt;td&gt;North&lt;/td&gt;
&lt;td&gt;700&lt;/td&gt;
&lt;td&gt;2700&lt;/td&gt;
&lt;td&gt;25.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dev&lt;/td&gt;
&lt;td&gt;North&lt;/td&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;td&gt;2700&lt;/td&gt;
&lt;td&gt;14.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Eve&lt;/td&gt;
&lt;td&gt;South&lt;/td&gt;
&lt;td&gt;1200&lt;/td&gt;
&lt;td&gt;2150&lt;/td&gt;
&lt;td&gt;55.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Finn&lt;/td&gt;
&lt;td&gt;South&lt;/td&gt;
&lt;td&gt;650&lt;/td&gt;
&lt;td&gt;2150&lt;/td&gt;
&lt;td&gt;30.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gus&lt;/td&gt;
&lt;td&gt;South&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;td&gt;2150&lt;/td&gt;
&lt;td&gt;14.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;All seven rows survive. &lt;code&gt;PARTITION BY region&lt;/code&gt; splits the rows into groups, like &lt;code&gt;GROUP BY&lt;/code&gt; does, but instead of collapsing each group into one row, it gives every row access to its whole group. With an empty &lt;code&gt;OVER ()&lt;/code&gt;, the window is the entire result.&lt;/p&gt;
&lt;figure class=&#34;diagram&#34;&gt;&lt;figcaption&gt;PARTITION BY splits the rows into a North window and a South window; every row keeps its own values and gains its window&#39;s total, and with ORDER BY the sum becomes a running total&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&#34;the-anatomy-of-over&#34;&gt;The anatomy of OVER&lt;/h2&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(...)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OVER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PARTITION&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- which rows belong together (optional)&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- the order of rows inside each partition (optional)&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ROWS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BETWEEN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- which of those rows to use for this row (optional)&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Every part is optional, and each one changes the result. We&#39;ll go through them one at a time.&lt;/p&gt;
&lt;h2 id=&#34;ranking-row_number-rank-and-dense_rank&#34;&gt;Ranking: ROW_NUMBER, RANK and DENSE_RANK&lt;/h2&gt;
&lt;p&gt;Ranking functions need an &lt;code&gt;ORDER BY&lt;/code&gt; inside the window. They differ only in how they treat ties, and our data has one: Ben and Cleo both sold 700.&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rep&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ROW_NUMBER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OVER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DESC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;row_num&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;RANK&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OVER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DESC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rnk&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DENSE_RANK&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OVER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DESC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dense_rnk&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sales&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DESC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rep&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;rep&lt;/th&gt;
&lt;th&gt;amount&lt;/th&gt;
&lt;th&gt;row_num&lt;/th&gt;
&lt;th&gt;rnk&lt;/th&gt;
&lt;th&gt;dense_rnk&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Eve&lt;/td&gt;
&lt;td&gt;1200&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ana&lt;/td&gt;
&lt;td&gt;900&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ben&lt;/td&gt;
&lt;td&gt;700&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cleo&lt;/td&gt;
&lt;td&gt;700&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Finn&lt;/td&gt;
&lt;td&gt;650&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dev&lt;/td&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gus&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ROW_NUMBER&lt;/code&gt;&lt;/strong&gt; always gives 1, 2, 3, 4… with no ties. Which of Ben and Cleo gets 3 is up to the database, unless you add a tiebreaker such as &lt;code&gt;ORDER BY amount DESC, rep&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;RANK&lt;/code&gt;&lt;/strong&gt; gives ties the same number, then &lt;strong&gt;skips&lt;/strong&gt;: 3, 3, 5. Like a race where two runners share bronze.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;DENSE_RANK&lt;/code&gt;&lt;/strong&gt; gives ties the same number and &lt;strong&gt;doesn&#39;t skip&lt;/strong&gt;: 3, 3, 4.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;top-n-per-group&#34;&gt;Top N per group&lt;/h2&gt;
&lt;p&gt;&#34;The two best reps in each region&#34; is one of the most common reporting questions, and window functions make it easy. But you can&#39;t filter on a window function directly:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rep&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sales&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ROW_NUMBER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OVER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;-- Error: misuse of window function ROW_NUMBER()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Window functions are calculated &lt;strong&gt;after&lt;/strong&gt; &lt;code&gt;WHERE&lt;/code&gt;, &lt;code&gt;GROUP BY&lt;/code&gt; and &lt;code&gt;HAVING&lt;/code&gt;, just before the final &lt;code&gt;ORDER BY&lt;/code&gt; and &lt;code&gt;LIMIT&lt;/code&gt;. So you compute the rank in a subquery or CTE, then filter the result:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;WITH&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ranked&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rep&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;region&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;         &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ROW_NUMBER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OVER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PARTITION&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;region&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DESC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rep&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pos&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sales&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;region&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rep&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ranked&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pos&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;region&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;region&lt;/th&gt;
&lt;th&gt;pos&lt;/th&gt;
&lt;th&gt;rep&lt;/th&gt;
&lt;th&gt;amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;North&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Ana&lt;/td&gt;
&lt;td&gt;900&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;North&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Ben&lt;/td&gt;
&lt;td&gt;700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;South&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Eve&lt;/td&gt;
&lt;td&gt;1200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;South&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Finn&lt;/td&gt;
&lt;td&gt;650&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Use &lt;code&gt;RANK()&lt;/code&gt; instead of &lt;code&gt;ROW_NUMBER()&lt;/code&gt; if tied reps should &lt;em&gt;both&lt;/em&gt; make the list.&lt;/p&gt;
&lt;h2 id=&#34;running-totals&#34;&gt;Running totals&lt;/h2&gt;
&lt;p&gt;Add &lt;code&gt;ORDER BY&lt;/code&gt; to an aggregate&#39;s window and it becomes &lt;strong&gt;cumulative&lt;/strong&gt;: each row sums everything from the start of the partition up to itself.&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;month&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;revenue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SUM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;revenue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OVER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;month&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;running_total&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;monthly&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;month&lt;/th&gt;
&lt;th&gt;revenue&lt;/th&gt;
&lt;th&gt;running_total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2026-01&lt;/td&gt;
&lt;td&gt;12000&lt;/td&gt;
&lt;td&gt;12000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-02&lt;/td&gt;
&lt;td&gt;15000&lt;/td&gt;
&lt;td&gt;27000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-03&lt;/td&gt;
&lt;td&gt;11000&lt;/td&gt;
&lt;td&gt;38000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-04&lt;/td&gt;
&lt;td&gt;18000&lt;/td&gt;
&lt;td&gt;56000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-05&lt;/td&gt;
&lt;td&gt;21000&lt;/td&gt;
&lt;td&gt;77000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-06&lt;/td&gt;
&lt;td&gt;19500&lt;/td&gt;
&lt;td&gt;96500&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Add &lt;code&gt;PARTITION BY&lt;/code&gt; to restart the running total for each group, for example a year-to-date total that resets every January.&lt;/p&gt;
&lt;h2 id=&#34;comparing-with-the-previous-row-lag-and-lead&#34;&gt;Comparing with the previous row: LAG and LEAD&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;LAG(column)&lt;/code&gt; returns the value from the &lt;strong&gt;previous&lt;/strong&gt; row in the window, and &lt;code&gt;LEAD(column)&lt;/code&gt; from the &lt;strong&gt;next&lt;/strong&gt; one. Month-over-month change becomes a simple subtraction:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;month&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;revenue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LAG&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;revenue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OVER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;month&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;prev_month&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;revenue&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LAG&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;revenue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OVER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;month&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;change&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;monthly&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;month&lt;/th&gt;
&lt;th&gt;revenue&lt;/th&gt;
&lt;th&gt;prev_month&lt;/th&gt;
&lt;th&gt;change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2026-01&lt;/td&gt;
&lt;td&gt;12000&lt;/td&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-02&lt;/td&gt;
&lt;td&gt;15000&lt;/td&gt;
&lt;td&gt;12000&lt;/td&gt;
&lt;td&gt;3000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-03&lt;/td&gt;
&lt;td&gt;11000&lt;/td&gt;
&lt;td&gt;15000&lt;/td&gt;
&lt;td&gt;-4000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-04&lt;/td&gt;
&lt;td&gt;18000&lt;/td&gt;
&lt;td&gt;11000&lt;/td&gt;
&lt;td&gt;7000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-05&lt;/td&gt;
&lt;td&gt;21000&lt;/td&gt;
&lt;td&gt;18000&lt;/td&gt;
&lt;td&gt;3000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-06&lt;/td&gt;
&lt;td&gt;19500&lt;/td&gt;
&lt;td&gt;21000&lt;/td&gt;
&lt;td&gt;-1500&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;The first month has no previous row, so &lt;code&gt;LAG&lt;/code&gt; returns &lt;code&gt;NULL&lt;/code&gt;. &lt;code&gt;LAG(revenue, 1, 0)&lt;/code&gt; would return &lt;code&gt;0&lt;/code&gt; instead, and &lt;code&gt;LAG(revenue, 12)&lt;/code&gt; reaches back twelve rows, handy for year-over-year comparisons.&lt;/p&gt;
&lt;h2 id=&#34;window-frames-moving-averages&#34;&gt;Window frames: moving averages&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;frame&lt;/strong&gt; picks which rows of the partition count for the current row. A three-month moving average uses the current row and the two before it:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;month&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;revenue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ROUND&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AVG&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;revenue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OVER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;         &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;month&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;         &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ROWS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BETWEEN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PRECEDING&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AND&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;CURRENT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ROW&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;avg_3m&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;monthly&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;month&lt;/th&gt;
&lt;th&gt;revenue&lt;/th&gt;
&lt;th&gt;avg_3m&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2026-01&lt;/td&gt;
&lt;td&gt;12000&lt;/td&gt;
&lt;td&gt;12000.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-02&lt;/td&gt;
&lt;td&gt;15000&lt;/td&gt;
&lt;td&gt;13500.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-03&lt;/td&gt;
&lt;td&gt;11000&lt;/td&gt;
&lt;td&gt;12666.7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-04&lt;/td&gt;
&lt;td&gt;18000&lt;/td&gt;
&lt;td&gt;14666.7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-05&lt;/td&gt;
&lt;td&gt;21000&lt;/td&gt;
&lt;td&gt;16666.7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-06&lt;/td&gt;
&lt;td&gt;19500&lt;/td&gt;
&lt;td&gt;19500.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;The first two months average over fewer rows because there&#39;s nothing before them. Other useful frames are &lt;code&gt;ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING&lt;/code&gt; (a centred window) and &lt;code&gt;ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING&lt;/code&gt; (the whole partition).&lt;/p&gt;
&lt;div class=&#34;admonition warning&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;The default frame has a surprise&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;ORDER BY&lt;/code&gt; and no frame, the default is &lt;code&gt;RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW&lt;/code&gt;. &lt;code&gt;RANGE&lt;/code&gt; treats rows with &lt;strong&gt;equal&lt;/strong&gt; &lt;code&gt;ORDER BY&lt;/code&gt; values as one step. Run a running total over &lt;code&gt;amount DESC&lt;/code&gt; and Ben and Cleo, both at 700, &lt;em&gt;both&lt;/em&gt; show 3500, because each one includes the other. Write &lt;code&gt;ROWS UNBOUNDED PRECEDING&lt;/code&gt; when you want a strict row-by-row total.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The same default explains a classic puzzle: &lt;code&gt;LAST_VALUE(revenue) OVER (ORDER BY month)&lt;/code&gt; returns the &lt;strong&gt;current&lt;/strong&gt; row&#39;s value, not the last month&#39;s, because the default frame ends at the current row. Give it the whole partition with &lt;code&gt;ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;windows-over-grouped-results&#34;&gt;Windows over grouped results&lt;/h2&gt;
&lt;p&gt;Window functions run after &lt;code&gt;GROUP BY&lt;/code&gt;, so they can work on the groups themselves. Here, each region&#39;s share of the grand total: the inner &lt;code&gt;SUM(amount)&lt;/code&gt; is the region total, and the outer &lt;code&gt;SUM(...) OVER ()&lt;/code&gt; adds up those totals across all regions:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;region&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SUM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;total&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ROUND&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SUM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SUM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SUM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;amount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OVER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pct_of_all&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sales&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;GROUP&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;region&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;region&lt;/th&gt;
&lt;th&gt;total&lt;/th&gt;
&lt;th&gt;pct_of_all&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;North&lt;/td&gt;
&lt;td&gt;2700&lt;/td&gt;
&lt;td&gt;55.7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;South&lt;/td&gt;
&lt;td&gt;2150&lt;/td&gt;
&lt;td&gt;44.3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;div class=&#34;admonition tip&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;Name a window you use more than once&lt;/p&gt;
&lt;p&gt;If several columns share the same window, define it once with a &lt;code&gt;WINDOW&lt;/code&gt; clause after &lt;code&gt;WHERE&lt;/code&gt;/&lt;code&gt;GROUP BY&lt;/code&gt;/&lt;code&gt;HAVING&lt;/code&gt;: &lt;code&gt;SELECT ..., FIRST_VALUE(revenue) OVER w, LAG(revenue) OVER w FROM monthly WINDOW w AS (ORDER BY month);&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;cheat-sheet&#34;&gt;Cheat sheet&lt;/h2&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;You want&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A group total next to every row&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SUM(x) OVER (PARTITION BY g)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A unique row number per group&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ROW_NUMBER() OVER (PARTITION BY g ORDER BY x)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A ranking with ties&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RANK()&lt;/code&gt; or &lt;code&gt;DENSE_RANK()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A running total&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SUM(x) OVER (ORDER BY t ROWS UNBOUNDED PRECEDING)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The previous / next row&#39;s value&lt;/td&gt;
&lt;td&gt;&lt;code&gt;LAG(x)&lt;/code&gt; / &lt;code&gt;LEAD(x)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A moving average&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AVG(x) OVER (ORDER BY t ROWS BETWEEN n PRECEDING AND CURRENT ROW)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Top N per group&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ROW_NUMBER()&lt;/code&gt; in a CTE, then &lt;code&gt;WHERE pos &amp;lt;= N&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&#34;recap&#34;&gt;Recap&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A window function is a normal function followed by &lt;strong&gt;&lt;code&gt;OVER (...)&lt;/code&gt;&lt;/strong&gt;. It calculates across related rows but &lt;strong&gt;keeps every row&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;PARTITION BY&lt;/code&gt;&lt;/strong&gt; splits rows into groups, &lt;strong&gt;&lt;code&gt;ORDER BY&lt;/code&gt;&lt;/strong&gt; orders them inside each group, and the &lt;strong&gt;frame&lt;/strong&gt; (&lt;code&gt;ROWS BETWEEN ...&lt;/code&gt;) picks the rows used for each calculation.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ROW_NUMBER&lt;/code&gt;, &lt;code&gt;RANK&lt;/code&gt; and &lt;code&gt;DENSE_RANK&lt;/code&gt; differ only in how they handle &lt;strong&gt;ties&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Window functions run &lt;strong&gt;after &lt;code&gt;WHERE&lt;/code&gt; and &lt;code&gt;GROUP BY&lt;/code&gt;&lt;/strong&gt;, so filter on them in a CTE or subquery.&lt;/li&gt;
&lt;li&gt;Watch the &lt;strong&gt;default frame&lt;/strong&gt;: use &lt;code&gt;ROWS&lt;/code&gt; for strict running totals, and give &lt;code&gt;LAST_VALUE&lt;/code&gt; the whole partition.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once your queries summarise millions of rows, speed starts to matter; &lt;a href=&#34;/en/posts/sql-indexes-query-plans/&#34;&gt;SQL Indexes and Query Plans&lt;/a&gt; is the natural next step.&lt;/p&gt;</content:encoded>
    </item>
    <item>
      <title>SQL Indexes and Query Plans: Making Slow Queries Fast</title>
      <link>https://cryo-networks.com/en/posts/sql-indexes-query-plans/</link>
      <guid isPermaLink="true">https://cryo-networks.com/en/posts/sql-indexes-query-plans/</guid>
      <pubDate>Thu, 29 Oct 2026 09:00:00 +0000</pubDate>
      <category>SQL</category>
      <description>Learn how B-tree indexes really work, read query plans with EXPLAIN, choose the right column order for composite indexes, and spot the mistakes that silently disable an index.</description>
      <content:encoded>&lt;p&gt;A query that returns instantly on your laptop can take seconds in production. Usually nothing is wrong with the query itself: the table just grew from a thousand rows to a few million, and the database is reading &lt;strong&gt;every single one&lt;/strong&gt; of them to find the handful you asked for.&lt;/p&gt;
&lt;p&gt;Indexes fix that, but only the &lt;em&gt;right&lt;/em&gt; index, and only if your query lets the database use it. In this article you&#39;ll build a million-row table, watch a query go from 100 ms to under a millisecond, and learn to read the query plan that tells you &lt;em&gt;why&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;We&#39;ll use SQLite because it&#39;s already installed almost everywhere and needs no server. The ideas carry over directly to PostgreSQL and MySQL; there&#39;s a translation table at the end. If JOINs are still new to you, start with &lt;a href=&#34;/en/posts/sql-joins-explained/&#34;&gt;SQL JOINs Explained with Simple Examples&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;set-up-a-million-rows&#34;&gt;Set up a million rows&lt;/h2&gt;
&lt;p&gt;Open a new database with &lt;code&gt;sqlite3 shop.db&lt;/code&gt; and paste this in. It creates an &lt;code&gt;orders&lt;/code&gt; table and fills it with one million random orders from 50,000 customers:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;TABLE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;INTEGER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;PRIMARY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;KEY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customer_id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;INTEGER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NOT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;TEXT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NOT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;total&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;REAL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NOT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;created_at&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;TEXT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NOT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;WITH&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;RECURSIVE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;UNION&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ALL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1000000&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;INSERT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INTO&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customer_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;total&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;created_at&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;abs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;%&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;50000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;CASE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;abs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;%&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHEN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;THEN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;pending&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHEN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;THEN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;cancelled&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ELSE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;shipped&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;END&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;abs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;%&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;50000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;datetime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;2024-01-01&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;+&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;abs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;%&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;86400000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39; seconds&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Then turn on timing with &lt;code&gt;.timer on&lt;/code&gt;. Every query will now report how long it took.&lt;/p&gt;
&lt;h2 id=&#34;reading-a-query-plan&#34;&gt;Reading a query plan&lt;/h2&gt;
&lt;p&gt;Prefix any query with &lt;code&gt;EXPLAIN QUERY PLAN&lt;/code&gt; and SQLite shows its strategy instead of running it:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;EXPLAIN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QUERY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PLAN&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customer_id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4242&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;QUERY PLAN
`--SCAN orders&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;SCAN&lt;/code&gt;&lt;/strong&gt; means a full table scan: SQLite reads all one million rows and checks each one. Run the query itself and, in our test, it took about &lt;strong&gt;100 ms&lt;/strong&gt; to return a handful of rows. That doesn&#39;t sound like much until a page runs it twenty times, or a thousand users load that page at once.&lt;/p&gt;
&lt;h2 id=&#34;how-a-b-tree-index-works&#34;&gt;How a B-tree index works&lt;/h2&gt;
&lt;p&gt;An index is a separate, &lt;strong&gt;sorted&lt;/strong&gt; copy of one or more columns, where each entry points back to its row. Databases store it as a &lt;strong&gt;B-tree&lt;/strong&gt;: a shallow tree of pages, each holding hundreds of sorted keys. To find &lt;code&gt;4242&lt;/code&gt;, the database reads the root page, follows the pointer for the range containing 4242, and repeats until it reaches a leaf page. Because each page fans out to hundreds of children, a million rows need only three or four levels.&lt;/p&gt;
&lt;figure class=&#34;diagram&#34;&gt;&lt;figcaption&gt;A B-tree lookup for customer_id 4242 reads one root page, one branch page and one leaf page, then fetches only the matching rows&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Create one and look at the plan again:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INDEX&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idx_orders_customer&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customer_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;EXPLAIN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QUERY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PLAN&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customer_id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4242&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;QUERY PLAN
`--SEARCH orders USING INDEX idx_orders_customer (customer_id=?)&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;SEARCH ... USING INDEX&lt;/code&gt;&lt;/strong&gt; means SQLite walks the index to the matching entries, then fetches just those rows from the table. The same query now runs in &lt;strong&gt;under a millisecond&lt;/strong&gt;, roughly a hundred times faster, and the gap keeps growing with the table: a scan doubles in cost when the table doubles, while an index lookup barely notices.&lt;/p&gt;
&lt;h2 id=&#34;composite-indexes-column-order-is-everything&#34;&gt;Composite indexes: column order is everything&lt;/h2&gt;
&lt;p&gt;Real queries filter on more than one column. Here&#39;s a typical one: a customer&#39;s recent orders, newest last.&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customer_id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4242&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AND&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;created_at&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;2025-01-01&amp;#39;&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;created_at&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;With only &lt;code&gt;idx_orders_customer&lt;/code&gt;, the plan has a second line:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;QUERY PLAN
|--SEARCH orders USING INDEX idx_orders_customer (customer_id=?)
`--USE TEMP B-TREE FOR ORDER BY&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;USE TEMP B-TREE FOR ORDER BY&lt;/code&gt;&lt;/strong&gt; means the rows come back unsorted and SQLite has to sort them itself. A &lt;strong&gt;composite index&lt;/strong&gt; on both columns solves the filter &lt;em&gt;and&lt;/em&gt; the sort at once:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INDEX&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idx_orders_customer_created&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customer_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;created_at&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;QUERY PLAN
`--SEARCH orders USING INDEX idx_orders_customer_created (customer_id=? AND created_at&amp;gt;?)&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Think of a composite index like a phone book sorted by last name, then first name. Inside the &#34;4242&#34; section, entries are already sorted by &lt;code&gt;created_at&lt;/code&gt;, so the database jumps to the first date it needs and reads forward. No sort step.&lt;/p&gt;
&lt;p&gt;Now try the columns the other way round. Drop the two customer indexes first, so SQLite can&#39;t pick them instead:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;DROP&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INDEX&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idx_orders_customer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;DROP&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INDEX&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idx_orders_customer_created&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INDEX&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idx_orders_created_customer&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;created_at&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customer_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;QUERY PLAN
`--SEARCH orders USING INDEX idx_orders_created_customer (created_at&amp;gt;?)&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;It still says &lt;code&gt;SEARCH&lt;/code&gt;, so it looks fine. But it took &lt;strong&gt;95 ms&lt;/strong&gt; in our test, almost as slow as no index at all. Look inside the parentheses: only &lt;code&gt;created_at&amp;gt;?&lt;/code&gt; uses the index. The range covers about two thirds of all orders, and SQLite has to check every one of those entries for &lt;code&gt;customer_id = 4242&lt;/code&gt;. Drop this index (&lt;code&gt;DROP INDEX idx_orders_created_customer;&lt;/code&gt;) before moving on.&lt;/p&gt;
&lt;div class=&#34;admonition tip&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;The rule for column order&lt;/p&gt;
&lt;p&gt;Put columns compared with &lt;strong&gt;&lt;code&gt;=&lt;/code&gt;&lt;/strong&gt; first, then &lt;strong&gt;one&lt;/strong&gt; column used for a range (&lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;BETWEEN&lt;/code&gt;) or for &lt;code&gt;ORDER BY&lt;/code&gt; last. An index on &lt;code&gt;(a, b, c)&lt;/code&gt; can also serve queries on &lt;code&gt;a&lt;/code&gt; alone or on &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt;, but &lt;strong&gt;not&lt;/strong&gt; queries on &lt;code&gt;b&lt;/code&gt; or &lt;code&gt;c&lt;/code&gt; alone. This is called the &lt;em&gt;leftmost prefix&lt;/em&gt; rule.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;admonition warning&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;SEARCH doesn&#39;t mean fast&lt;/p&gt;
&lt;p&gt;Always read &lt;em&gt;which&lt;/em&gt; columns appear inside the parentheses. A &lt;code&gt;SEARCH&lt;/code&gt; that only uses a broad range can be nearly as slow as a &lt;code&gt;SCAN&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;covering-indexes-skip-the-table-entirely&#34;&gt;Covering indexes: skip the table entirely&lt;/h2&gt;
&lt;p&gt;Even with a perfect index, each match costs an extra jump from the index into the table to fetch the rest of the row. If the index already contains &lt;strong&gt;every column the query needs&lt;/strong&gt;, the database can skip the table altogether:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INDEX&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idx_orders_customer_created_total&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customer_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;created_at&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;total&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;EXPLAIN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QUERY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PLAN&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;created_at&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;total&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customer_id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4242&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;QUERY PLAN
`--SEARCH orders USING COVERING INDEX idx_orders_customer_created_total (customer_id=?)&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;COVERING INDEX&lt;/code&gt;&lt;/strong&gt; is the best plan you can get for a lookup. It matters most for queries that return many rows, such as reports and dashboards. It&#39;s another good reason to avoid &lt;code&gt;SELECT *&lt;/code&gt;: asking for fewer columns makes a covering index possible.&lt;/p&gt;
&lt;h2 id=&#34;mistakes-that-silently-disable-an-index&#34;&gt;Mistakes that silently disable an index&lt;/h2&gt;
&lt;p&gt;An index only helps if the database can compare your condition directly against the sorted values. Wrap the column in a function or expression, and it can&#39;t. Say we add a plain index on &lt;code&gt;created_at&lt;/code&gt; and look for one day&#39;s orders:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INDEX&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idx_orders_created&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;created_at&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

&lt;span class=&#34;c1&#34;&gt;-- ✗ a full SCAN: date() has to be computed for every one of the million entries (≈600 ms)&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;created_at&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;2025-03-14&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

&lt;span class=&#34;c1&#34;&gt;-- ✓ a SEARCH on idx_orders_created (created_at&amp;gt;? AND created_at&amp;lt;?) (&amp;lt;1 ms)&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;created_at&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;2025-03-14&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AND&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;created_at&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;2025-03-15&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Both return the same count. The second form is called &lt;strong&gt;sargable&lt;/strong&gt; (from &lt;em&gt;Search ARGument ABLE&lt;/em&gt;): the column stands alone on one side of the comparison. Other common index killers:&lt;/p&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Instead of…&lt;/th&gt;
&lt;th&gt;Write…&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WHERE date(created_at) = &#39;2025-03-14&#39;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;WHERE created_at &amp;gt;= &#39;2025-03-14&#39; AND created_at &amp;lt; &#39;2025-03-15&#39;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WHERE price * 1.2 &amp;gt; 100&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;WHERE price &amp;gt; 100 / 1.2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WHERE lower(email) = &#39;ada@example.com&#39;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;store emails lowercased, or index the expression (below)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WHERE name LIKE &#39;%son&#39;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;a leading wildcard can&#39;t use a B-tree; consider full-text search&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;When you genuinely need to filter on an expression, index the expression itself. SQLite, PostgreSQL and MySQL 8 all support this:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INDEX&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idx_orders_day&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;created_at&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;-- now WHERE date(created_at) = &amp;#39;2025-03-14&amp;#39; uses: SEARCH orders USING INDEX idx_orders_day (&amp;lt;expr&amp;gt;=?)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;partial-indexes-index-only-the-rows-you-query&#34;&gt;Partial indexes: index only the rows you query&lt;/h2&gt;
&lt;p&gt;Suppose a background job keeps asking for pending orders, which are only 10% of the table. A &lt;strong&gt;partial index&lt;/strong&gt; includes just those rows:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INDEX&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idx_orders_pending&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;created_at&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;pending&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;EXPLAIN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QUERY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PLAN&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;created_at&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;pending&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AND&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;created_at&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;2024-02-01&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;QUERY PLAN
`--SEARCH orders USING INDEX idx_orders_pending (created_at&amp;lt;?)&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;In our database this index takes &lt;strong&gt;2.7 MB&lt;/strong&gt;, against &lt;strong&gt;27 MB&lt;/strong&gt; for a full index on &lt;code&gt;created_at&lt;/code&gt;. Smaller indexes are faster to search, fit in memory more easily, and cost less to update. PostgreSQL supports partial indexes too; MySQL doesn&#39;t.&lt;/p&gt;
&lt;h2 id=&#34;indexes-arent-free&#34;&gt;Indexes aren&#39;t free&lt;/h2&gt;
&lt;p&gt;Every index is a second data structure the database must keep sorted. Each &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt; of an indexed column, and &lt;code&gt;DELETE&lt;/code&gt; has to update every index on the table. In our test, inserting 200,000 more rows into the million-row table took about &lt;strong&gt;half a second&lt;/strong&gt; with no secondary indexes and over &lt;strong&gt;seven seconds&lt;/strong&gt; with three of them.&lt;/p&gt;
&lt;p&gt;So don&#39;t index every column &#34;just in case&#34;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Index columns that appear in frequent &lt;code&gt;WHERE&lt;/code&gt;, &lt;code&gt;JOIN&lt;/code&gt; and &lt;code&gt;ORDER BY&lt;/code&gt; clauses.&lt;/li&gt;
&lt;li&gt;Prefer one well-designed composite index over several single-column ones.&lt;/li&gt;
&lt;li&gt;Look for redundant indexes: &lt;code&gt;(customer_id)&lt;/code&gt; is redundant once &lt;code&gt;(customer_id, created_at)&lt;/code&gt; exists, because of the leftmost prefix rule.&lt;/li&gt;
&lt;li&gt;Columns with only a few distinct values (like &lt;code&gt;status&lt;/code&gt;) rarely make good indexes on their own. Use them in a partial index or as a later column in a composite one.&lt;/li&gt;
&lt;li&gt;After big data changes, run &lt;code&gt;ANALYZE&lt;/code&gt; so the planner has up-to-date statistics to choose between indexes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;reading-plans-in-postgresql-and-mysql&#34;&gt;Reading plans in PostgreSQL and MySQL&lt;/h2&gt;
&lt;p&gt;The vocabulary differs, but the questions are the same: &lt;em&gt;is it scanning or searching, which index is used, and is there an extra sort?&lt;/em&gt;&lt;/p&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;th&gt;SQLite&lt;/th&gt;
&lt;th&gt;PostgreSQL &lt;code&gt;EXPLAIN&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;MySQL &lt;code&gt;EXPLAIN&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reads the whole table&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SCAN orders&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Seq Scan on orders&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;type: ALL&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uses an index, then fetches rows&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SEARCH ... USING INDEX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Index Scan&lt;/code&gt; / &lt;code&gt;Bitmap Heap Scan&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;type: ref&lt;/code&gt; or &lt;code&gt;range&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Answers from the index alone&lt;/td&gt;
&lt;td&gt;&lt;code&gt;USING COVERING INDEX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Index Only Scan&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Extra: Using index&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extra sorting step&lt;/td&gt;
&lt;td&gt;&lt;code&gt;USE TEMP B-TREE FOR ORDER BY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Sort&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Extra: Using filesort&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;In PostgreSQL, use &lt;strong&gt;&lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt;&lt;/strong&gt; to actually run the query and see real row counts and timings next to the planner&#39;s estimates. When an estimate is wildly off from the real count, outdated statistics are the usual suspect.&lt;/p&gt;
&lt;h2 id=&#34;recap&#34;&gt;Recap&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;SCAN&lt;/code&gt;&lt;/strong&gt; reads every row; &lt;strong&gt;&lt;code&gt;SEARCH ... USING INDEX&lt;/code&gt;&lt;/strong&gt; jumps straight to the matches through a sorted B-tree.&lt;/li&gt;
&lt;li&gt;In composite indexes, put &lt;strong&gt;equality columns first&lt;/strong&gt;, then one range or sort column. Indexes only serve their &lt;strong&gt;leftmost prefix&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;covering index&lt;/strong&gt; answers the query without touching the table.&lt;/li&gt;
&lt;li&gt;Functions and arithmetic on an indexed column &lt;strong&gt;disable the index&lt;/strong&gt;. Rewrite the condition, or index the expression.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Partial indexes&lt;/strong&gt; keep indexes small when you only query a subset of rows.&lt;/li&gt;
&lt;li&gt;Every index slows down writes. Add indexes for real queries, and check the plan before and after.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The habit that matters most: before you add an index, run &lt;code&gt;EXPLAIN&lt;/code&gt;. After you add it, run &lt;code&gt;EXPLAIN&lt;/code&gt; again.&lt;/p&gt;</content:encoded>
    </item>
    <item>
      <title>Python Descriptors: The Protocol Behind Properties and Methods</title>
      <link>https://cryo-networks.com/en/posts/python-descriptors-explained/</link>
      <guid isPermaLink="true">https://cryo-networks.com/en/posts/python-descriptors-explained/</guid>
      <pubDate>Thu, 29 Oct 2026 09:00:00 +0000</pubDate>
      <category>Python</category>
      <description>@property, bound methods, cached_property and __slots__ all run on one small protocol. Learn how descriptors work, the exact attribute lookup order, and how to write your own.</description>
      <content:encoded>&lt;p&gt;Have you ever wondered how &lt;code&gt;@property&lt;/code&gt; turns a method into something that looks like a plain attribute? Or where &lt;code&gt;self&lt;/code&gt; actually comes from when you call &lt;code&gt;obj.method()&lt;/code&gt;? Or how Django model fields and SQLAlchemy columns know which object they belong to?&lt;/p&gt;
&lt;p&gt;They all rely on the same small mechanism: the &lt;strong&gt;descriptor protocol&lt;/strong&gt;. It&#39;s one of the few genuinely &#34;magic&#34; parts of Python, and once you understand it you can read the language&#39;s internals and write cleaner, reusable attribute logic of your own.&lt;/p&gt;
&lt;p&gt;If decorators are still new to you, read &lt;a href=&#34;/en/posts/python-decorators-explained/&#34;&gt;Python Decorators Explained Step by Step&lt;/a&gt; first. We&#39;ll use them below.&lt;/p&gt;
&lt;h2 id=&#34;what-is-a-descriptor&#34;&gt;What is a descriptor?&lt;/h2&gt;
&lt;p&gt;A descriptor is any object whose class defines at least one of these methods:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;fm&#34;&gt;__get__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;objtype&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;# called when the attribute is read&lt;/span&gt;
&lt;span class=&#34;fm&#34;&gt;__set__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;          &lt;span class=&#34;c1&#34;&gt;# called when it is assigned&lt;/span&gt;
&lt;span class=&#34;fm&#34;&gt;__delete__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;              &lt;span class=&#34;c1&#34;&gt;# called on `del obj.attr`&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;The catch: it only works when the descriptor is stored as a &lt;strong&gt;class attribute&lt;/strong&gt;. When Python looks up &lt;code&gt;obj.attr&lt;/code&gt; and finds a descriptor on the class, it calls the descriptor&#39;s method instead of returning the descriptor itself.&lt;/p&gt;
&lt;h2 id=&#34;your-first-descriptor-a-validated-attribute&#34;&gt;Your first descriptor: a validated attribute&lt;/h2&gt;
&lt;p&gt;Say every &lt;code&gt;Product&lt;/code&gt; needs a positive &lt;code&gt;price&lt;/code&gt; and &lt;code&gt;quantity&lt;/code&gt;. With properties you&#39;d write the same getter and setter twice. With a descriptor you write the rule once and reuse it:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Positive&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;&amp;quot;&amp;quot;&amp;quot;A number that must stay above zero.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;__set_name__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;owner&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;public_name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;
        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;private_name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;_&amp;quot;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__get__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;objtype&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;          &lt;span class=&#34;c1&#34;&gt;# accessed on the class: Product.price&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;getattr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;private_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__set__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;raise&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;ValueError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;public_name&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; must be positive, got &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;!r}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;nb&#34;&gt;setattr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;private_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;


&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Product&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;price&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Positive&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;quantity&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Positive&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__init__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;price&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;quantity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;
        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;price&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;price&lt;/span&gt;          &lt;span class=&#34;c1&#34;&gt;# goes through Positive.__set__&lt;/span&gt;
        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;quantity&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;quantity&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Product&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Keyboard&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;49.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;price&lt;/span&gt;
&lt;span class=&#34;mf&#34;&gt;49.0&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;vars&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Keyboard&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;_price&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;49.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;_quantity&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;quantity&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;span class=&#34;ne&#34;&gt;ValueError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;quantity&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;must&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;be&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;positive&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;got&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Three details are worth noticing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;__set_name__&lt;/code&gt;&lt;/strong&gt; (Python 3.6+) is called once, when the class is created, with the attribute name the descriptor was assigned to. That&#39;s how one &lt;code&gt;Positive&lt;/code&gt; class knows whether it&#39;s guarding &lt;code&gt;price&lt;/code&gt; or &lt;code&gt;quantity&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;obj is None&lt;/code&gt;&lt;/strong&gt; means the attribute was read from the class (&lt;code&gt;Product.price&lt;/code&gt;) rather than an instance. Returning the descriptor itself is the convention, and it keeps tools like &lt;code&gt;help()&lt;/code&gt; working.&lt;/li&gt;
&lt;li&gt;The real values live &lt;strong&gt;on the instance&lt;/strong&gt;, under &lt;code&gt;_price&lt;/code&gt; and &lt;code&gt;_quantity&lt;/code&gt;. The descriptor only holds configuration.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;the-attribute-lookup-order&#34;&gt;The attribute lookup order&lt;/h2&gt;
&lt;p&gt;To use descriptors confidently you need the exact rules &lt;code&gt;object.__getattribute__&lt;/code&gt; follows for &lt;code&gt;obj.attr&lt;/code&gt;. Descriptors come in two kinds:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;data descriptor&lt;/strong&gt; defines &lt;code&gt;__set__&lt;/code&gt; or &lt;code&gt;__delete__&lt;/code&gt; (&lt;code&gt;Positive&lt;/code&gt; and &lt;code&gt;property&lt;/code&gt; are data descriptors).&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;non-data descriptor&lt;/strong&gt; defines only &lt;code&gt;__get__&lt;/code&gt; (plain functions are non-data descriptors).&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&#34;diagram&#34;&gt;&lt;figcaption&gt;Python checks a data descriptor on the class first, then the instance dictionary, then non-data descriptors and class attributes, and finally __getattr__&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;The difference matters because the &lt;strong&gt;instance dictionary sits between them&lt;/strong&gt;. You can prove it:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;DataDesc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__get__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;objtype&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;data descriptor&amp;quot;&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__set__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;raise&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;AttributeError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;read-only&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;


&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;NonDataDesc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__get__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;objtype&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;non-data descriptor&amp;quot;&lt;/span&gt;


&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Demo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DataDesc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;NonDataDesc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;


&lt;span class=&#34;n&#34;&gt;d&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Demo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;vm&#34;&gt;__dict__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;a&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;instance dict&amp;quot;&lt;/span&gt;     &lt;span class=&#34;c1&#34;&gt;# bypass __set__ and write directly&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;vm&#34;&gt;__dict__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;b&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;instance dict&amp;quot;&lt;/span&gt;

&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;# data descriptor   ← beats the instance dict&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;# instance dict     ← beats the non-data descriptor&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;That one rule explains a surprising amount of Python&#39;s behaviour, as the next sections show.&lt;/p&gt;
&lt;h2 id=&#34;methods-are-descriptors&#34;&gt;Methods are descriptors&lt;/h2&gt;
&lt;p&gt;Functions have a &lt;code&gt;__get__&lt;/code&gt; method. When you read a function through an instance, &lt;code&gt;__get__&lt;/code&gt; returns a &lt;strong&gt;bound method&lt;/strong&gt;, a small object that remembers both the function and the instance. That&#39;s where &lt;code&gt;self&lt;/code&gt; comes from:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Greeter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;     &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;hello&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;         &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;hi&amp;quot;&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;g&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Greeter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Greeter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;vm&#34;&gt;__dict__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;hello&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;# the raw function, no descriptor magic&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Greeter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hello&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;at&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x7fc890f70160&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;fm&#34;&gt;__get__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Greeter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;            &lt;span class=&#34;c1&#34;&gt;# exactly what g.hello does&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bound&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;method&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Greeter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hello&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;of&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;__main__&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Greeter&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;object&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;at&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x7fc890f63760&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;g&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hello&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;fm&#34;&gt;__get__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Greeter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Because functions are &lt;em&gt;non-data&lt;/em&gt; descriptors, an instance attribute with the same name wins. That&#39;s why you can override a method on a single object (handy for mocks in tests):&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;g&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hello&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;lambda&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;patched&amp;quot;&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;g&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hello&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Greeter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hello&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;patched&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;hi&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;classmethod&lt;/code&gt; and &lt;code&gt;staticmethod&lt;/code&gt; are non-data descriptors too: their &lt;code&gt;__get__&lt;/code&gt; binds the class instead of the instance, or binds nothing at all.&lt;/p&gt;
&lt;h2 id=&#34;rebuilding-property&#34;&gt;Rebuilding @property&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;property&lt;/code&gt; is a data descriptor written in C, but a working pure-Python version fits on one screen:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;MyProperty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__init__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fget&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fset&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fget&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fget&lt;/span&gt;
        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fset&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__get__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;objtype&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fget&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__set__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fset&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;raise&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;AttributeError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;read-only attribute&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;setter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fget&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;# a new descriptor with both functions&lt;/span&gt;


&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Temperature&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__init__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;celsius&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;celsius&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;celsius&lt;/span&gt;

    &lt;span class=&#34;nd&#34;&gt;@MyProperty&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;fahrenheit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;celsius&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;9&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;32&lt;/span&gt;

    &lt;span class=&#34;nd&#34;&gt;@fahrenheit&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setter&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;fahrenheit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;celsius&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;9&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Temperature&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fahrenheit&lt;/span&gt;
&lt;span class=&#34;mf&#34;&gt;212.0&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fahrenheit&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;32&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;celsius&lt;/span&gt;
&lt;span class=&#34;mf&#34;&gt;0.0&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;@MyProperty&lt;/code&gt; is just &lt;code&gt;fahrenheit = MyProperty(fahrenheit)&lt;/code&gt;. Then &lt;code&gt;@fahrenheit.setter&lt;/code&gt; builds a second descriptor holding both functions and rebinds the name to it. Because &lt;code&gt;MyProperty&lt;/code&gt; always defines &lt;code&gt;__set__&lt;/code&gt;, it&#39;s a data descriptor even without a setter, which is why a read-only property can&#39;t be shadowed by assigning to the instance.&lt;/p&gt;
&lt;h2 id=&#34;cached_property-using-the-lookup-order-on-purpose&#34;&gt;cached_property: using the lookup order on purpose&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;functools.cached_property&lt;/code&gt; is a clever use of the rule from earlier. It&#39;s a &lt;strong&gt;non-data&lt;/strong&gt; descriptor: the first access runs your method and stores the result in the instance&#39;s &lt;code&gt;__dict__&lt;/code&gt; under the same name. From then on the instance dictionary wins the lookup, so &lt;code&gt;__get__&lt;/code&gt; is never called again:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;functools&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cached_property&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Report&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;     &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__init__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rows&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;         &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rows&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rows&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;     &lt;span class=&#34;nd&#34;&gt;@cached_property&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;     &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;total&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;         &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;computing…&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;         &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;sum&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rows&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;r&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Report&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;total&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;computing&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;…&lt;/span&gt;
&lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;total&lt;/span&gt;          &lt;span class=&#34;c1&#34;&gt;# served straight from r.__dict__&lt;/span&gt;
&lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;vars&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;rows&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;total&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;del&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;total&lt;/span&gt;      &lt;span class=&#34;c1&#34;&gt;# clear the cache; the next access recomputes&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;admonition note&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;cached_property needs an instance &lt;strong&gt;dict&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Since the cache lives in &lt;code&gt;obj.__dict__&lt;/code&gt;, &lt;code&gt;cached_property&lt;/code&gt; doesn&#39;t work on classes that define &lt;code&gt;__slots__&lt;/code&gt; without including &lt;code&gt;&#34;__dict__&#34;&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;the-classic-bug-storing-state-on-the-descriptor&#34;&gt;The classic bug: storing state on the descriptor&lt;/h2&gt;
&lt;p&gt;There&#39;s only &lt;strong&gt;one&lt;/strong&gt; descriptor object per class attribute, shared by every instance. Store per-object data on &lt;code&gt;self&lt;/code&gt; inside a descriptor and all instances end up sharing it:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Broken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__get__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;objtype&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__set__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;     &lt;span class=&#34;c1&#34;&gt;# stored on the descriptor, which every instance shares&lt;/span&gt;


&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Account&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;balance&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Broken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;


&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Account&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Account&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;balance&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;balance&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;balance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;# 5  ← a&amp;#39;s balance was overwritten by b&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Store values on &lt;code&gt;obj&lt;/code&gt; instead, as &lt;code&gt;Positive&lt;/code&gt; does with &lt;code&gt;setattr(obj, self.private_name, value)&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;admonition danger&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;Avoid infinite recursion&lt;/p&gt;
&lt;p&gt;Inside &lt;code&gt;__set__&lt;/code&gt;, never write &lt;code&gt;setattr(obj, self.public_name, value)&lt;/code&gt;. Assigning to the &lt;em&gt;public&lt;/em&gt; name calls &lt;code&gt;__set__&lt;/code&gt; again, forever. Always store under a different name, or write to &lt;code&gt;obj.__dict__&lt;/code&gt; directly.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;where-else-youll-meet-descriptors&#34;&gt;Where else you&#39;ll meet descriptors&lt;/h2&gt;
&lt;p&gt;Once you know the pattern you&#39;ll spot it everywhere:&lt;/p&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Descriptor kind&lt;/th&gt;
&lt;th&gt;What &lt;code&gt;__get__&lt;/code&gt; does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Plain methods&lt;/td&gt;
&lt;td&gt;non-data&lt;/td&gt;
&lt;td&gt;returns a bound method&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;classmethod&lt;/code&gt; / &lt;code&gt;staticmethod&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;non-data&lt;/td&gt;
&lt;td&gt;binds the class / binds nothing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;property&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;data&lt;/td&gt;
&lt;td&gt;calls the getter function&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;functools.cached_property&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;non-data&lt;/td&gt;
&lt;td&gt;computes once, caches in &lt;code&gt;obj.__dict__&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;__slots__&lt;/code&gt; entries&lt;/td&gt;
&lt;td&gt;data&lt;/td&gt;
&lt;td&gt;reads a fixed slot in the object&#39;s memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Django fields, SQLAlchemy columns&lt;/td&gt;
&lt;td&gt;depends on the field&lt;/td&gt;
&lt;td&gt;load the value from the database, convert or track it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;You can even see the &lt;code&gt;__slots__&lt;/code&gt; ones directly:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Point&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;     &lt;span class=&#34;vm&#34;&gt;__slots__&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Point&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;vm&#34;&gt;__dict__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;member&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;x&amp;#39;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;of&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Point&amp;#39;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;objects&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;recap&#34;&gt;Recap&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;descriptor&lt;/strong&gt; is a class attribute whose class defines &lt;code&gt;__get__&lt;/code&gt;, &lt;code&gt;__set__&lt;/code&gt; or &lt;code&gt;__delete__&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data descriptors&lt;/strong&gt; (with &lt;code&gt;__set__&lt;/code&gt; or &lt;code&gt;__delete__&lt;/code&gt;) beat the instance dictionary; &lt;strong&gt;non-data descriptors&lt;/strong&gt; lose to it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Methods&lt;/strong&gt;, &lt;code&gt;property&lt;/code&gt;, &lt;code&gt;classmethod&lt;/code&gt;, &lt;code&gt;staticmethod&lt;/code&gt;, &lt;code&gt;cached_property&lt;/code&gt; and &lt;code&gt;__slots__&lt;/code&gt; are all built on descriptors.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;&lt;code&gt;__set_name__&lt;/code&gt;&lt;/strong&gt; to learn the attribute&#39;s name, and store per-instance values &lt;strong&gt;on the instance&lt;/strong&gt;, never on the descriptor.&lt;/li&gt;
&lt;li&gt;Reach for a custom descriptor when the same attribute logic (validation, conversion, lazy loading) repeats across several attributes or classes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The best way to make this stick: open a REPL, write a descriptor that prints in each method, and watch exactly when Python calls it.&lt;/p&gt;</content:encoded>
    </item>
    <item>
      <title>Linux Processes Under the Hood: fork, exec, Signals and Zombies</title>
      <link>https://cryo-networks.com/en/posts/linux-processes-signals/</link>
      <guid isPermaLink="true">https://cryo-networks.com/en/posts/linux-processes-signals/</guid>
      <pubDate>Thu, 29 Oct 2026 09:00:00 +0000</pubDate>
      <category>Linux</category>
      <description>What really happens when you run a command: fork and exec, exit codes and wait, how signals stop programs (and how to shut down gracefully), why zombies appear, and why PID 1 in a container ignores your Ctrl+C.</description>
      <content:encoded>&lt;p&gt;You type &lt;code&gt;ls&lt;/code&gt; and press &lt;kbd&gt;Enter&lt;/kbd&gt;. A list of files appears, and the prompt comes back. Behind that, the kernel has created a process, replaced its program, collected its exit status and cleaned up after it, using just a handful of system calls that every program on Linux relies on.&lt;/p&gt;
&lt;p&gt;Knowing those calls explains a lot of things that otherwise seem mysterious: why &lt;code&gt;cd&lt;/code&gt; can&#39;t be an external program, what exit code 137 means, why &lt;code&gt;kill -9&lt;/code&gt; should be your last resort, where zombie processes come from, and why a container sometimes ignores &lt;code&gt;docker stop&lt;/code&gt; for exactly ten seconds. The examples use Python&#39;s &lt;code&gt;os&lt;/code&gt; module, which maps almost one-to-one onto the underlying system calls, plus standard tools like &lt;code&gt;ps&lt;/code&gt;. You&#39;ll get more out of it if you&#39;ve written a few shell scripts; see &lt;a href=&#34;/en/posts/bash-scripts-that-dont-break/&#34;&gt;Writing Bash Scripts That Don&#39;t Break&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;fork-and-exec-how-every-program-starts&#34;&gt;fork and exec: how every program starts&lt;/h2&gt;
&lt;p&gt;Linux creates new processes in two separate steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;fork()&lt;/code&gt;&lt;/strong&gt; makes a copy of the calling process. Parent and child continue from the same line of code, with the same memory contents, open files and environment. The only difference is &lt;code&gt;fork()&lt;/code&gt;&#39;s return value: &lt;code&gt;0&lt;/code&gt; in the child, and the child&#39;s PID in the parent.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;exec()&lt;/code&gt;&lt;/strong&gt; replaces the program running in the current process with a new one. The PID stays the same; the code, memory and stack are thrown away and loaded fresh from the new executable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A shell runs &lt;code&gt;ls&lt;/code&gt; by forking itself, then making the &lt;strong&gt;child&lt;/strong&gt; exec &lt;code&gt;ls&lt;/code&gt;, while the &lt;strong&gt;parent&lt;/strong&gt; waits. Here&#39;s the same thing in Python:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;os&lt;/span&gt;

&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;parent: my PID is &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;getpid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;flush&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;pid&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fork&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pid&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;# Child: a copy of the parent. Replace it with the `ls` program.&lt;/span&gt;
    &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;child:  my PID is &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;getpid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;, my parent is &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;getppid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;flush&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;execvp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;ls&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;ls&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;-d&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;/tmp&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
    &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;never printed: exec only returns if it fails&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;# Parent: wait for the child and collect its exit status.&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;waitpid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;parent: child &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pid&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; exited with code &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;waitstatus_to_exitcode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;$ python3 forkexec.py
parent: my PID is 726920
child:  my PID is 726922, my parent is 726920
/tmp
parent: child 726922 exited with code 0&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;admonition note&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;Why flush=True matters&lt;/p&gt;
&lt;p&gt;&lt;code&gt;exec&lt;/code&gt; replaces the whole program, including Python&#39;s output buffer. If the output goes to a pipe or a file, it&#39;s buffered, and anything not yet flushed is silently lost. The same applies to C&#39;s &lt;code&gt;printf&lt;/code&gt;. Flush before you fork or exec.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The split into two calls is what makes Unix shells so flexible. Between &lt;code&gt;fork&lt;/code&gt; and &lt;code&gt;exec&lt;/code&gt;, the child is still running the shell&#39;s code, so it can rearrange its own environment first: redirect file descriptors for &lt;code&gt;&amp;gt; file&lt;/code&gt; or &lt;code&gt;|&lt;/code&gt;, change directory, set environment variables. Then it execs the program, which inherits all of it.&lt;/p&gt;
&lt;p&gt;It also explains why &lt;code&gt;cd&lt;/code&gt; must be a &lt;strong&gt;shell builtin&lt;/strong&gt;. A child process can only change its own working directory, and that change disappears when it exits. To change the &lt;em&gt;shell&#39;s&lt;/em&gt; directory, the shell has to do it itself.&lt;/p&gt;
&lt;figure class=&#34;diagram&#34;&gt;&lt;figcaption&gt;The life of a process: the shell (PID 4100) forks a child (PID 4101), the child execs ls, ls exits with code 0, the child stays a zombie until the shell calls wait, and then it disappears&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&#34;exit-codes-and-wait&#34;&gt;Exit codes and wait&lt;/h2&gt;
&lt;p&gt;When a process ends, it leaves behind an &lt;strong&gt;exit status&lt;/strong&gt; for its parent: 0 for success, 1–255 for failure, by convention. The parent collects it with &lt;code&gt;wait()&lt;/code&gt; or &lt;code&gt;waitpid()&lt;/code&gt;, and the shell puts it in &lt;code&gt;$?&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If the process was killed by a &lt;strong&gt;signal&lt;/strong&gt;, the shell reports &lt;code&gt;128 + the signal number&lt;/code&gt;. These come up constantly in logs and CI output:&lt;/p&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Exit code&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;, …&lt;/td&gt;
&lt;td&gt;The program reported an error (meaning varies by program)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;126&lt;/code&gt; / &lt;code&gt;127&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Command found but not executable / command not found&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;130&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Killed by SIGINT (2): someone pressed &lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;C&lt;/kbd&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;137&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Killed by SIGKILL (9): often the &lt;strong&gt;out-of-memory killer&lt;/strong&gt;, or a container exceeding its memory limit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;143&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Killed by SIGTERM (15): a normal shutdown request, e.g. &lt;code&gt;docker stop&lt;/code&gt; or a deploy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&#34;signals-notifying-a-process&#34;&gt;Signals: notifying a process&lt;/h2&gt;
&lt;p&gt;A &lt;strong&gt;signal&lt;/strong&gt; is a small asynchronous notification sent to a process, by the kernel or by another process with &lt;code&gt;kill&lt;/code&gt;. Despite the name, &lt;code&gt;kill&lt;/code&gt; sends &lt;em&gt;any&lt;/em&gt; signal; SIGTERM is just the default. The ones you&#39;ll meet most:&lt;/p&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;Number&lt;/th&gt;
&lt;th&gt;Default action&lt;/th&gt;
&lt;th&gt;Typical source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SIGHUP&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;terminate&lt;/td&gt;
&lt;td&gt;Terminal closed; many daemons reload their config on it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIGINT&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;terminate&lt;/td&gt;
&lt;td&gt;&lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;C&lt;/kbd&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIGQUIT&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;terminate + core dump&lt;/td&gt;
&lt;td&gt;&lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;\&lt;/kbd&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIGKILL&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;terminate, &lt;strong&gt;can&#39;t be caught&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kill -9&lt;/code&gt;, the OOM killer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIGTERM&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;terminate&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kill&lt;/code&gt;, &lt;code&gt;systemctl stop&lt;/code&gt;, &lt;code&gt;docker stop&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIGCHLD&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;ignore&lt;/td&gt;
&lt;td&gt;A child process exited or stopped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIGSTOP / SIGTSTP&lt;/td&gt;
&lt;td&gt;19 / 20&lt;/td&gt;
&lt;td&gt;pause&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kill -STOP&lt;/code&gt; / &lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;Z&lt;/kbd&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;(The numbers are for x86 and ARM Linux; run &lt;code&gt;kill -l&lt;/code&gt; to see the full list on your system.)&lt;/p&gt;
&lt;p&gt;A process can &lt;strong&gt;handle&lt;/strong&gt; most signals with its own function, or ignore them. Two can never be caught or ignored: &lt;strong&gt;SIGKILL&lt;/strong&gt; and &lt;strong&gt;SIGSTOP&lt;/strong&gt;. The kernel acts on those directly.&lt;/p&gt;
&lt;h2 id=&#34;graceful-shutdown&#34;&gt;Graceful shutdown&lt;/h2&gt;
&lt;p&gt;That&#39;s why you should send &lt;strong&gt;SIGTERM first&lt;/strong&gt; and SIGKILL only as a last resort. SIGTERM lets a program finish the request it&#39;s serving, flush data to disk, close database connections and remove lock files. SIGKILL stops it mid-instruction, with no chance to clean up.&lt;/p&gt;
&lt;p&gt;Handling SIGTERM takes a few lines:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;signal&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;time&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;running&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;stop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;signum&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;frame&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;global&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;running&lt;/span&gt;
    &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;got &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;signal&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Signals&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;signum&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;, finishing the current job...&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;flush&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;running&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;signal&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;signal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;signal&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SIGTERM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;stop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;running&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sleep&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;# pretend to work&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;clean exit&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;flush&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;$ python3 worker.py &amp;amp;
$ kill %1
got SIGTERM, finishing the current job...
clean exit&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;The handler only sets a flag; the main loop checks it and exits at a safe point. Doing real work inside a signal handler is risky, because it can interrupt your program anywhere. Bash scripts get the same ability with &lt;code&gt;trap &#39;cleanup&#39; TERM&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Service managers follow the same pattern: &lt;code&gt;systemctl stop&lt;/code&gt; sends SIGTERM, waits (90 seconds by default), then sends SIGKILL. &lt;code&gt;docker stop&lt;/code&gt; does the same with a 10-second grace period.&lt;/p&gt;
&lt;h2 id=&#34;zombies&#34;&gt;Zombies&lt;/h2&gt;
&lt;p&gt;When a child exits, the kernel can&#39;t throw it away completely yet: the parent might still want its exit status. So the process becomes a &lt;strong&gt;zombie&lt;/strong&gt;. It has no memory or open files any more, just a process-table entry holding its PID and exit status, waiting for the parent to call &lt;code&gt;wait()&lt;/code&gt;. Collecting the status is called &lt;strong&gt;reaping&lt;/strong&gt;, and it removes the zombie.&lt;/p&gt;
&lt;p&gt;A parent that never calls &lt;code&gt;wait()&lt;/code&gt; leaves zombies behind. Let&#39;s make one:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;time&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;pid&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fork&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pid&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_exit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;          &lt;span class=&#34;c1&#34;&gt;# the child exits immediately...&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;child PID: &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pid&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sleep&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;60&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;           &lt;span class=&#34;c1&#34;&gt;# ...but the parent never calls wait()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;$ python3 zombie.py &amp;amp;
child PID: 726905
$ ps -o pid,ppid,stat,cmd --ppid $!
    PID    PPID STAT CMD
 726905  726903 Z    [python3] &amp;lt;defunct&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;Z&lt;/code&gt; in the &lt;code&gt;STAT&lt;/code&gt; column and &lt;code&gt;&amp;lt;defunct&amp;gt;&lt;/code&gt; mark a zombie. A few zombies are harmless, but each one holds a PID, and a buggy server that leaks zombies can eventually exhaust the PID limit, at which point nothing on the machine can start a new process.&lt;/p&gt;
&lt;p&gt;You &lt;strong&gt;can&#39;t kill a zombie&lt;/strong&gt;: it&#39;s already dead, so signals have no effect. Fix the parent so it calls &lt;code&gt;wait()&lt;/code&gt;, or terminate the parent. When a parent dies, its children (living or zombie) are &lt;strong&gt;re-parented&lt;/strong&gt; to PID 1 (or to the nearest process marked as a &#34;subreaper&#34;), which reaps them automatically. On a normal system, PID 1 is &lt;code&gt;systemd&lt;/code&gt;, and it&#39;s very good at reaping.&lt;/p&gt;
&lt;h2 id=&#34;pid-1-is-special-and-containers-make-it-your-problem&#34;&gt;PID 1 is special, and containers make it your problem&lt;/h2&gt;
&lt;p&gt;Inside a container, there&#39;s no &lt;code&gt;systemd&lt;/code&gt;. Whatever command the image starts becomes &lt;strong&gt;PID 1&lt;/strong&gt; in the container&#39;s PID namespace, and PID 1 has two extra responsibilities:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The kernel doesn&#39;t apply default signal actions to it.&lt;/strong&gt; A signal that PID 1 hasn&#39;t installed a handler for is simply ignored. A normal program that relies on SIGTERM&#39;s default &#34;terminate&#34; action will &lt;strong&gt;ignore &lt;code&gt;docker stop&lt;/code&gt;&lt;/strong&gt;, sit there for the 10-second grace period, and get SIGKILLed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It inherits every orphan&lt;/strong&gt;, so it has to reap them, or zombies pile up.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can see the first one without Docker. &lt;code&gt;unshare&lt;/code&gt; starts a program as PID 1 in a new PID namespace:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;unshare&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--fork&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--pid&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--mount-proc&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;python3&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-c&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;import time; time.sleep(30)&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;
$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;pid&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;pgrep&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-n&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-f&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;time.sleep&amp;#39;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# the Python process, as seen from outside&lt;/span&gt;
$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;kill&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-TERM&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$pid&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;             &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# nothing happens: PID 1 has no SIGTERM handler&lt;/span&gt;
$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;kill&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-KILL&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$pid&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;             &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# this works: SIGKILL from the parent namespace&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;The fixes are simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Run a tiny init process as PID 1: &lt;code&gt;docker run --init&lt;/code&gt; adds &lt;a href=&#34;https://github.com/krallin/tini&#34;&gt;tini&lt;/a&gt;, which forwards signals to your program and reaps zombies. Kubernetes users can add &lt;code&gt;tini&lt;/code&gt; (or &lt;code&gt;dumb-init&lt;/code&gt;) to the image as the entrypoint.&lt;/li&gt;
&lt;li&gt;Make your program handle SIGTERM explicitly, like &lt;code&gt;worker.py&lt;/code&gt; above.&lt;/li&gt;
&lt;li&gt;Use the &lt;strong&gt;exec form&lt;/strong&gt; in Dockerfiles, &lt;code&gt;CMD [&#34;python3&#34;, &#34;app.py&#34;]&lt;/code&gt;, not &lt;code&gt;CMD python3 app.py&lt;/code&gt;. The shell form starts &lt;code&gt;/bin/sh -c &#34;python3 app.py&#34;&lt;/code&gt;, so the &lt;em&gt;shell&lt;/em&gt; becomes PID 1, and it doesn&#39;t pass SIGTERM on to your program.&lt;/li&gt;
&lt;li&gt;In entrypoint scripts, end with &lt;strong&gt;&lt;code&gt;exec &#34;$@&#34;&lt;/code&gt;&lt;/strong&gt;. &lt;code&gt;exec&lt;/code&gt; replaces the shell with your program (the same exec as above), so your program becomes PID 1 and receives signals directly.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;inspecting-processes&#34;&gt;Inspecting processes&lt;/h2&gt;
&lt;p&gt;Every process has a directory under &lt;strong&gt;&lt;code&gt;/proc/&amp;lt;PID&amp;gt;/&lt;/code&gt;&lt;/strong&gt;, and most tools just read from it:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;ps&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-eo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pid,ppid,stat,etime,cmd&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--forest&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# every process, as a tree&lt;/span&gt;
pstree&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-p&lt;span class=&#34;w&#34;&gt;                                 &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# a compact tree with PIDs&lt;/span&gt;
cat&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;/proc/1234/status&lt;span class=&#34;w&#34;&gt;                     &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# state, parent, memory, threads, signal masks&lt;/span&gt;
tr&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;\0&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;lt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;/proc/1234/cmdline&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# the full command line&lt;/span&gt;
ls&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-l&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;/proc/1234/fd&lt;span class=&#34;w&#34;&gt;                       &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# open files, sockets and pipes&lt;/span&gt;
cat&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;/proc/1234/environ&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;tr&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;\0&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# its environment (needs permission)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;The &lt;code&gt;STAT&lt;/code&gt; column&#39;s first letter tells you what a process is doing:&lt;/p&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;R&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Running, or ready to run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;S&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sleeping, waiting for something (most processes, most of the time)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;D&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Uninterruptible sleep, usually waiting on disk or network storage; even SIGKILL waits until it returns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;T&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stopped (SIGSTOP, &lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;Z&lt;/kbd&gt;, or a debugger)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Z&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Zombie: exited, waiting to be reaped&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;A process stuck in &lt;code&gt;D&lt;/code&gt; that can&#39;t be killed almost always points to a storage problem (a hung NFS mount, a failing disk), not to the program itself.&lt;/p&gt;
&lt;h2 id=&#34;recap&#34;&gt;Recap&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;New processes are made with &lt;strong&gt;&lt;code&gt;fork()&lt;/code&gt;&lt;/strong&gt; (copy the current process) and &lt;strong&gt;&lt;code&gt;exec()&lt;/code&gt;&lt;/strong&gt; (replace its program). The gap between them is where shells set up redirection and pipes.&lt;/li&gt;
&lt;li&gt;The parent collects the exit status with &lt;strong&gt;&lt;code&gt;wait()&lt;/code&gt;&lt;/strong&gt;; exit codes above 128 mean &#34;killed by signal (code − 128)&#34;: &lt;strong&gt;130&lt;/strong&gt; Ctrl+C, &lt;strong&gt;137&lt;/strong&gt; SIGKILL/OOM, &lt;strong&gt;143&lt;/strong&gt; SIGTERM.&lt;/li&gt;
&lt;li&gt;Send &lt;strong&gt;SIGTERM&lt;/strong&gt; and give programs time to shut down; &lt;strong&gt;SIGKILL&lt;/strong&gt; can&#39;t be caught and skips all cleanup.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zombies&lt;/strong&gt; are exited children nobody has reaped; fix or kill the parent, not the zombie.&lt;/li&gt;
&lt;li&gt;In containers, &lt;strong&gt;PID 1&lt;/strong&gt; ignores signals it doesn&#39;t handle and must reap orphans: use &lt;code&gt;--init&lt;/code&gt;/tini, exec-form &lt;code&gt;CMD&lt;/code&gt;, and &lt;code&gt;exec &#34;$@&#34;&lt;/code&gt; in entrypoints.&lt;/li&gt;
&lt;/ul&gt;</content:encoded>
    </item>
    <item>
      <title>Interactive Rebase: Clean Up Your Commits Before You Share Them</title>
      <link>https://cryo-networks.com/en/posts/git-interactive-rebase/</link>
      <guid isPermaLink="true">https://cryo-networks.com/en/posts/git-interactive-rebase/</guid>
      <pubDate>Thu, 29 Oct 2026 09:00:00 +0000</pubDate>
      <category>Git</category>
      <description>Turn a messy trail of WIP commits into a clean, reviewable history with git rebase -i. Squash, reword, reorder, split and drop commits, and let fixup commits do the cleanup for you.</description>
      <content:encoded>&lt;p&gt;Real work is messy. You commit a half-finished form, then &#34;WIP&#34;, then &#34;fix typo&#34;, then a debug print you forgot to remove. That&#39;s fine while you&#39;re working, but it&#39;s a poor history to hand to a reviewer, and an even worse one to find six months later in &lt;code&gt;git log&lt;/code&gt; or &lt;code&gt;git blame&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Interactive rebase&lt;/strong&gt; lets you rewrite your own branch before you share it: combine commits, rewrite their messages, change their order, split them up or delete them. It&#39;s one of the most useful Git skills to learn once you&#39;re comfortable with branches. If you aren&#39;t yet, read &lt;a href=&#34;/en/posts/git-branching-workflow/&#34;&gt;A Practical Git Branching Workflow for Beginners&lt;/a&gt; first.&lt;/p&gt;
&lt;h2 id=&#34;the-messy-branch&#34;&gt;The messy branch&lt;/h2&gt;
&lt;p&gt;Here&#39;s a feature branch with five commits on top of &lt;code&gt;main&lt;/code&gt;:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;log&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--oneline&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;main..
746aec7&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;debug&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;print
451c410&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;fix&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;typo
63d1135&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;Add&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;password&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;validation
0ce6ff7&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;WIP
4430f61&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;Add&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;login&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;form&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;main..&lt;/code&gt; means &#34;commits on this branch that aren&#39;t on &lt;code&gt;main&lt;/code&gt;&#34;. What we&#39;d like to share is just two commits: &lt;em&gt;Add login form&lt;/em&gt; (including the WIP and the typo fix, which both touched the form) and &lt;em&gt;Add password validation&lt;/em&gt;, without the debug print.&lt;/p&gt;
&lt;h2 id=&#34;starting-an-interactive-rebase&#34;&gt;Starting an interactive rebase&lt;/h2&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;rebase&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-i&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;main&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Git opens your editor with a &lt;strong&gt;todo list&lt;/strong&gt;, one line per commit:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;pick 4430f61 Add login form
pick 0ce6ff7 WIP
pick 63d1135 Add password validation
pick 451c410 fix typo
pick 746aec7 debug print

# Rebase 1c893f9..746aec7 onto 1c893f9 (5 commands)
#
# Commands:
# p, pick &amp;lt;commit&amp;gt; = use commit
# r, reword &amp;lt;commit&amp;gt; = use commit, but edit the commit message
# e, edit &amp;lt;commit&amp;gt; = use commit, but stop for amending
# s, squash &amp;lt;commit&amp;gt; = use commit, but meld into previous commit
# f, fixup [-C | -c] &amp;lt;commit&amp;gt; = like &amp;quot;squash&amp;quot; but keep only the previous
#                    commit&amp;#39;s log message, unless -C is used, ...
# d, drop &amp;lt;commit&amp;gt; = remove commit
# ...&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;admonition warning&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;The todo list is upside down compared to git log&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git log&lt;/code&gt; shows the newest commit first. The todo list shows the &lt;strong&gt;oldest first&lt;/strong&gt;, because that&#39;s the order Git will replay them in. Read it from top to bottom like a script.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Git will start from &lt;code&gt;main&lt;/code&gt; and run each line in turn. Change the word at the start of a line to change what happens to that commit:&lt;/p&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pick&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keep the commit as it is&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reword&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keep the changes, but stop to edit the message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;edit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply the commit, then pause so you can change it (amend, split…)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;squash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Merge into the commit above, and combine both messages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fixup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Merge into the commit above, and throw this commit&#39;s message away&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;drop&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete the commit (removing the line does the same)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;exec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a shell command at this point, and stop if it fails&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&#34;squash-reorder-and-drop&#34;&gt;Squash, reorder and drop&lt;/h2&gt;
&lt;p&gt;Edit the todo list like this, then save and close the editor:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;pick 4430f61 Add login form
fixup 0ce6ff7 WIP
fixup 451c410 fix typo
pick 63d1135 Add password validation
drop 746aec7 debug print&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;We changed three things: the two small commits become &lt;code&gt;fixup&lt;/code&gt;s of &lt;em&gt;Add login form&lt;/em&gt;, &lt;em&gt;fix typo&lt;/em&gt; &lt;strong&gt;moved up&lt;/strong&gt; so that it sits right under the commit it fixes, and the debug print is dropped.&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;rebase&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-i&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;main
Successfully&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;rebased&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;and&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;updated&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;refs/heads/login-form.

$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;log&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--oneline&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;main..
486c6b2&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;Add&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;password&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;validation
24c6dfe&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;Add&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;login&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;form&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;diagram&#34;&gt;&lt;figcaption&gt;The todo list read from top to bottom: two fixups melt into Add login form, Add password validation is kept, and the debug print is dropped, leaving two clean commits&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Notice the hashes changed, even for &lt;em&gt;Add password validation&lt;/em&gt;, whose content we didn&#39;t touch. Rebase never edits commits: it creates &lt;strong&gt;new&lt;/strong&gt; ones with the same changes. That detail matters when we push, below.&lt;/p&gt;
&lt;div class=&#34;admonition tip&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;squash or fixup?&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;squash&lt;/code&gt; when the second message has something worth keeping: Git opens the editor with both messages so you can merge them. Use &lt;code&gt;fixup&lt;/code&gt; for &#34;oops&#34; commits whose message you&#39;d delete anyway.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;fixup-commits-clean-up-as-you-go&#34;&gt;Fixup commits: clean up as you go&lt;/h2&gt;
&lt;p&gt;Reordering lines by hand gets tedious. If you already know which commit a change belongs to, tell Git when you commit:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;c1&#34;&gt;# Spotted a bug in &amp;quot;Add password validation&amp;quot;? Fix it, then:&lt;/span&gt;
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;commit&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-a&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--fixup&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;486c6b2&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Git creates a commit called &lt;code&gt;fixup! Add password validation&lt;/code&gt;. Later, &lt;code&gt;--autosquash&lt;/code&gt; moves every such commit under its target and marks it &lt;code&gt;fixup&lt;/code&gt; for you:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;rebase&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-i&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--autosquash&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;main&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;pick 24c6dfe Add login form
pick 486c6b2 Add password validation
fixup 4eb2adb fixup! Add password validation&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Just save and close. To make this the default, run &lt;code&gt;git config --global rebase.autoSquash true&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;splitting-a-commit-with-edit&#34;&gt;Splitting a commit with edit&lt;/h2&gt;
&lt;p&gt;Sometimes one commit does two unrelated things. Mark it &lt;code&gt;edit&lt;/code&gt;, and Git pauses right after applying it:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;edit 9a1c2d3 Add login form and fix footer links
pick 3f7e8b1 Add password validation&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;reset&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;HEAD^&lt;span class=&#34;w&#34;&gt;                 &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# undo the commit, keep its changes as unstaged edits&lt;/span&gt;
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;add&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;login.html
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;commit&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-m&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Add login form&amp;quot;&lt;/span&gt;
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;add&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;footer.html
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;commit&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-m&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Fix footer links&amp;quot;&lt;/span&gt;
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;rebase&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--continue&lt;span class=&#34;w&#34;&gt;           &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# carry on with the rest of the todo list&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;git add -p&lt;/code&gt; is handy here when both changes are in the same file: it asks about each block of changes one at a time.&lt;/p&gt;
&lt;h2 id=&#34;when-theres-a-conflict&#34;&gt;When there&#39;s a conflict&lt;/h2&gt;
&lt;p&gt;Reordering or dropping commits can produce conflicts, exactly like a merge. Git stops and tells you which files conflict. You have three options:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;c1&#34;&gt;# fix the files, then mark them as resolved and continue&lt;/span&gt;
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;add&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;login.html
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;rebase&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--continue

git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;rebase&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--skip&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# drop the commit that conflicts and move on&lt;/span&gt;
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;rebase&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--abort&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# give up: put the branch back exactly as it was&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;--abort&lt;/code&gt; is your safety net. As long as the rebase hasn&#39;t finished, nothing has changed for real.&lt;/p&gt;
&lt;div class=&#34;admonition tip&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;Run your tests on every commit&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git rebase -i main --exec &#34;npm test&#34;&lt;/code&gt; (or &lt;code&gt;-x&lt;/code&gt;) adds an &lt;code&gt;exec&lt;/code&gt; line after every commit. If the tests fail anywhere, the rebase stops on the commit that broke them. It&#39;s a great final check that each commit works on its own, which is what makes &lt;code&gt;git bisect&lt;/code&gt; useful later.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;rebase-without-moving-your-branch&#34;&gt;Rebase without moving your branch&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git rebase -i main&lt;/code&gt; also moves your branch on top of the &lt;strong&gt;latest&lt;/strong&gt; &lt;code&gt;main&lt;/code&gt;. Usually that&#39;s what you want, but if &lt;code&gt;main&lt;/code&gt; has moved a lot, you&#39;ll get conflicts that have nothing to do with your cleanup. To tidy commits in place, keeping the same starting point:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;rebase&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-i&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--keep-base&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;main&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;You can also name the starting point directly: &lt;code&gt;git rebase -i HEAD~3&lt;/code&gt; lets you edit the last three commits.&lt;/p&gt;
&lt;h2 id=&#34;pushing-a-rewritten-branch&#34;&gt;Pushing a rewritten branch&lt;/h2&gt;
&lt;p&gt;Because the commits are new, a branch you pushed before the rebase no longer matches the remote, and a normal &lt;code&gt;git push&lt;/code&gt; is rejected. Use:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;push&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--force-with-lease&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Unlike &lt;code&gt;--force&lt;/code&gt;, &lt;code&gt;--force-with-lease&lt;/code&gt; refuses to overwrite the remote branch if someone else pushed to it since you last fetched, so you can&#39;t wipe out a teammate&#39;s work by accident.&lt;/p&gt;
&lt;div class=&#34;admonition danger&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;The golden rule of rebasing&lt;/p&gt;
&lt;p&gt;Only rewrite commits that nobody else has built on. Cleaning up your own feature branch before (or during) a pull request is fine. Rebasing &lt;code&gt;main&lt;/code&gt;, or a branch your teammates have pulled, leaves them with copies of the old commits and a painful merge.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;undoing-a-rebase&#34;&gt;Undoing a rebase&lt;/h2&gt;
&lt;p&gt;Finished a rebase and regret it? Git keeps the old position of your branch in &lt;code&gt;ORIG_HEAD&lt;/code&gt;:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;reset&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--hard&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;ORIG_HEAD&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;This only works right after the rebase, because other commands also update &lt;code&gt;ORIG_HEAD&lt;/code&gt;. Further back, the reflog remembers every place your branch has been (&lt;code&gt;git reflog&lt;/code&gt;). &lt;a href=&#34;/en/posts/git-internals-objects-refs/&#34;&gt;How Git Really Works&lt;/a&gt; explains why the old commits are still there.&lt;/p&gt;
&lt;h2 id=&#34;recap&#34;&gt;Recap&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;git rebase -i main&lt;/code&gt; opens a &lt;strong&gt;todo list&lt;/strong&gt; of your branch&#39;s commits, &lt;strong&gt;oldest first&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Change &lt;code&gt;pick&lt;/code&gt; to &lt;code&gt;reword&lt;/code&gt;, &lt;code&gt;edit&lt;/code&gt;, &lt;code&gt;squash&lt;/code&gt;, &lt;code&gt;fixup&lt;/code&gt; or &lt;code&gt;drop&lt;/code&gt;, and move lines to reorder commits.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git commit --fixup=&amp;lt;commit&amp;gt;&lt;/code&gt; plus &lt;code&gt;--autosquash&lt;/code&gt; makes cleanup almost automatic.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--abort&lt;/code&gt; stops a rebase in progress; &lt;code&gt;ORIG_HEAD&lt;/code&gt; and the reflog undo a finished one.&lt;/li&gt;
&lt;li&gt;Rebasing creates new commits, so push with &lt;code&gt;--force-with-lease&lt;/code&gt;, and &lt;strong&gt;never rebase commits other people have built on&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;</content:encoded>
    </item>
    <item>
      <title>How Git Really Works: Blobs, Trees, Commits and Refs</title>
      <link>https://cryo-networks.com/en/posts/git-internals-objects-refs/</link>
      <guid isPermaLink="true">https://cryo-networks.com/en/posts/git-internals-objects-refs/</guid>
      <pubDate>Thu, 29 Oct 2026 09:00:00 +0000</pubDate>
      <category>Git</category>
      <description>Open the .git folder and see Git for what it is: a content-addressed object store with movable pointers on top. Build a commit by hand and finally understand rebase, reset and the reflog.</description>
      <content:encoded>&lt;p&gt;Most people learn Git as a list of commands to memorise. That works until something goes wrong: a rebase rewrites history you didn&#39;t expect, a &lt;code&gt;reset --hard&lt;/code&gt; &#34;deletes&#34; a day&#39;s work, or you find yourself in a detached HEAD. Then the commands stop making sense.&lt;/p&gt;
&lt;p&gt;Underneath, Git is surprisingly simple. It&#39;s a &lt;strong&gt;key-value store of immutable objects&lt;/strong&gt;, plus a handful of &lt;strong&gt;movable pointers&lt;/strong&gt; called refs. Once you&#39;ve seen both with your own eyes, every Git command becomes &#34;add some objects, then move a pointer&#34;.&lt;/p&gt;
&lt;p&gt;You&#39;ll get the most from this if you&#39;re comfortable with everyday branching. If not, read &lt;a href=&#34;/en/posts/git-branching-workflow/&#34;&gt;A Practical Git Branching Workflow for Beginners&lt;/a&gt; first.&lt;/p&gt;
&lt;h2 id=&#34;content-addressed-storage&#34;&gt;Content-addressed storage&lt;/h2&gt;
&lt;p&gt;Git stores everything (file contents, directories, commits) as &lt;strong&gt;objects&lt;/strong&gt;, and each object&#39;s name is the SHA-1 hash of its content. Ask Git to hash some text:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;hello&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;hash-object&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--stdin
ce013625030ba8dba906f756967f9e9ca394464a&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Run that on any machine, in any repository, and you get the same hash. Git hashes a small header (the word &lt;code&gt;blob&lt;/code&gt;, a space, the size in bytes and a null byte) followed by the content, and you can reproduce it without Git at all:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;blob 6\0hello\n&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sha1sum
ce013625030ba8dba906f756967f9e9ca394464a&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;-&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;This design has big consequences. Identical content is stored only once, however many files or commits contain it. And since the name depends on the content, an object can never change: change one byte and you get a different object with a different name.&lt;/p&gt;
&lt;h2 id=&#34;the-four-object-types&#34;&gt;The four object types&lt;/h2&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Stores&lt;/th&gt;
&lt;th&gt;Points to&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;blob&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;the contents of one file (no name, no permissions)&lt;/td&gt;
&lt;td&gt;nothing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;tree&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;one directory: names, modes and the hashes of its entries&lt;/td&gt;
&lt;td&gt;blobs and other trees&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;commit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;a snapshot: author, committer, date and message&lt;/td&gt;
&lt;td&gt;one tree and its parent commit(s)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;tag&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;an annotated tag: tagger, message, optional signature&lt;/td&gt;
&lt;td&gt;usually a commit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Let&#39;s make a tiny repository and look at real objects:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;init&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-b&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;main&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;demo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;demo
mkdir&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;src
&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;# Demo&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;README.md
&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;print(&amp;quot;hello&amp;quot;)&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;src/app.py
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;add&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;.&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;commit&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-m&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;First commit&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;That one commit created five objects, stored as compressed files under &lt;code&gt;.git/objects/&lt;/code&gt;, named by the first two characters of the hash, then the rest:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;find&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;.git/objects&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-type&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;f
.git/objects/08/05455a24b6c68fbc38d0fa5d121f735984285d
.git/objects/11/b15b1a4584b08fa423a57964bdbf018b0da0d5
.git/objects/16/ae6a2afcc370fa1a06bc31edcacc3a7f2c41cc
.git/objects/60/546c2588e9d75d822392d6f296e1fd99f04c96
.git/objects/ef/6ad6746727fa1d0efcf4a020ec3caeda5ccf64&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;admonition note&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;Your hashes will mostly match&lt;/p&gt;
&lt;p&gt;Blobs and trees depend only on content, so if you used the same files, those four hashes will be identical on your machine. The commit hash won&#39;t be: it includes your name, email and the exact time.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;reading-objects-with-cat-file&#34;&gt;Reading objects with cat-file&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git cat-file -p&lt;/code&gt; pretty-prints any object. Start from the commit that &lt;code&gt;HEAD&lt;/code&gt; points to:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;cat-file&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-p&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;HEAD
tree&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;60546c2588e9d75d822392d6f296e1fd99f04c96
author&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;Ada&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;lt;ada@example.com&amp;gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1793256281&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;+0000
committer&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;Ada&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;lt;ada@example.com&amp;gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1793256281&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;+0000

First&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;commit&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;A commit is just a few lines of text. It names exactly one &lt;strong&gt;tree&lt;/strong&gt;, the snapshot of the whole project. Follow it:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;cat-file&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-p&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;60546c2
&lt;span class=&#34;m&#34;&gt;100644&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;blob&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;0805455a24b6c68fbc38d0fa5d121f735984285d&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;README.md
&lt;span class=&#34;m&#34;&gt;040000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;tree&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;ef6ad6746727fa1d0efcf4a020ec3caeda5ccf64&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;src

$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;cat-file&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-p&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;ef6ad67
&lt;span class=&#34;m&#34;&gt;100644&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;blob&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;11b15b1a4584b08fa423a57964bdbf018b0da0d5&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;app.py

$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;cat-file&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-p&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;11b15b1
print&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;hello&amp;quot;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Notice where the file &lt;em&gt;names&lt;/em&gt; live: in the trees, not in the blobs. Renaming a file creates a new tree but reuses the same blob.&lt;/p&gt;
&lt;p&gt;Now change one file and commit again:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;print(&amp;quot;hello, world&amp;quot;)&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;src/app.py
$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;commit&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-am&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Greet the world&amp;quot;&lt;/span&gt;
$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;cat-file&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-p&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;HEAD
tree&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;17b6cd9de955edb28d8a9b0e4668f588ba3c664a
parent&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;16ae6a2afcc370fa1a06bc31edcacc3a7f2c41cc
...
$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;cat-file&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-p&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;HEAD^{tree}&amp;#39;&lt;/span&gt;
&lt;span class=&#34;m&#34;&gt;100644&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;blob&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;0805455a24b6c68fbc38d0fa5d121f735984285d&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;README.md
&lt;span class=&#34;m&#34;&gt;040000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;tree&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;b6dcf64039da33b42fb025d2cc49c6d070c65686&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;src&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;The new commit has a &lt;strong&gt;&lt;code&gt;parent&lt;/code&gt;&lt;/strong&gt; line pointing to the first commit. Its root tree has a new &lt;code&gt;src&lt;/code&gt; tree (because &lt;code&gt;app.py&lt;/code&gt; changed), but &lt;code&gt;README.md&lt;/code&gt; still points to the very same blob, &lt;code&gt;0805455&lt;/code&gt;. Unchanged files cost nothing.&lt;/p&gt;
&lt;figure class=&#34;diagram&#34;&gt;&lt;figcaption&gt;Two commits: the second points to its parent and to a new tree; the unchanged README blob is shared by both trees, and main and HEAD point to the newest commit&lt;/figcaption&gt;&lt;/figure&gt;
&lt;div class=&#34;admonition tip&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;Snapshots, not diffs&lt;/p&gt;
&lt;p&gt;Each commit stores a full snapshot (a tree), not a list of changes. &lt;code&gt;git diff&lt;/code&gt; and &lt;code&gt;git show&lt;/code&gt; compute changes on the fly by comparing two trees. To save space on disk, &lt;code&gt;git gc&lt;/code&gt; later packs objects into &lt;em&gt;packfiles&lt;/em&gt; and delta-compresses similar ones, but that&#39;s a storage detail, invisible to everything else.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;refs-branches-are-41-byte-files&#34;&gt;Refs: branches are 41-byte files&lt;/h2&gt;
&lt;p&gt;If objects never change, how does a branch &#34;move&#34;? Branches aren&#39;t objects. They&#39;re &lt;strong&gt;refs&lt;/strong&gt;: tiny files that contain a commit hash.&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;cat&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;.git/HEAD
ref:&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;refs/heads/main

$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;cat&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;.git/refs/heads/main
ffb68609991f7ac36c595767d2d91545868364cb&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;HEAD&lt;/code&gt; usually holds the &lt;em&gt;name&lt;/em&gt; of the current branch, and the branch file holds a commit hash. When you commit, Git writes the new objects and then rewrites that one branch file with the new hash. That&#39;s all a commit is, from the branch&#39;s point of view.&lt;/p&gt;
&lt;p&gt;This explains several things that otherwise feel like magic:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Creating a branch is instant&lt;/strong&gt;, whatever the size of the repository: &lt;code&gt;git branch experiment&lt;/code&gt; just writes a new 41-byte file. (Git may later move refs into a single &lt;code&gt;.git/packed-refs&lt;/code&gt; file, which works the same way.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Detached HEAD&lt;/strong&gt; means &lt;code&gt;HEAD&lt;/code&gt; contains a commit hash directly instead of &lt;code&gt;ref: refs/heads/...&lt;/code&gt;. New commits still work, but no branch moves to remember them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tags&lt;/strong&gt; are refs under &lt;code&gt;refs/tags/&lt;/code&gt; that, by convention, never move.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;build-a-commit-by-hand&#34;&gt;Build a commit by hand&lt;/h2&gt;
&lt;p&gt;To prove there&#39;s nothing more to it, let&#39;s commit a file without &lt;code&gt;git add&lt;/code&gt; or &lt;code&gt;git commit&lt;/code&gt;, using only Git&#39;s low-level &#34;plumbing&#34; commands:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;c1&#34;&gt;# 1. Store the content as a blob (-w writes it into .git/objects)&lt;/span&gt;
&lt;span class=&#34;nv&#34;&gt;blob&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Made without git add&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;hash-object&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-w&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--stdin&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;c1&#34;&gt;# 2. Put it into the staging area (the index) under a file name&lt;/span&gt;
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;update-index&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--add&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--cacheinfo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;100644&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$blob&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;NOTES.txt

&lt;span class=&#34;c1&#34;&gt;# 3. Turn the index into a tree object&lt;/span&gt;
&lt;span class=&#34;nv&#34;&gt;tree&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;write-tree&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;c1&#34;&gt;# 4. Create a commit object pointing to that tree, with HEAD as its parent&lt;/span&gt;
&lt;span class=&#34;nv&#34;&gt;commit&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;commit-tree&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tree&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-p&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;HEAD&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-m&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Commit made by hand&amp;quot;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;c1&#34;&gt;# 5. Move the branch pointer&lt;/span&gt;
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;update-ref&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;refs/heads/main&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$commit&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;log&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--oneline
8e0d264&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;Commit&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;made&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;by&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;hand
ffb6860&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;Greet&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;the&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;world
16ae6a2&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;First&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;commit&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;A completely normal commit. Run &lt;code&gt;git status&lt;/code&gt; now and you&#39;ll see &lt;code&gt;NOTES.txt&lt;/code&gt; listed as deleted: it&#39;s in the commit and the index, but we never wrote it to the working directory. &lt;code&gt;git restore NOTES.txt&lt;/code&gt; fixes that. Those are Git&#39;s three places for a file: the &lt;strong&gt;object database&lt;/strong&gt;, the &lt;strong&gt;index&lt;/strong&gt;, and your &lt;strong&gt;working directory&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;what-this-explains&#34;&gt;What this explains&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Rebase creates new commits.&lt;/strong&gt; A commit&#39;s hash covers its parent&#39;s hash. When you rebase a branch, every commit gets a new parent, so Git has to create brand-new commits with new hashes, even if the code changes are identical. The old commits still exist; the branch simply points somewhere else now. That&#39;s why rebasing commits that others have already pulled causes trouble: they still have the old ones.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;reset&lt;/code&gt; just moves a pointer.&lt;/strong&gt; &lt;code&gt;git reset --hard HEAD~2&lt;/code&gt; points the current branch two commits back (and updates your files to match). The commits themselves aren&#39;t deleted.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The reflog remembers where pointers were.&lt;/strong&gt; Every time &lt;code&gt;HEAD&lt;/code&gt; or a branch moves, Git logs it. So a &#34;lost&#34; commit is usually one command away:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;reset&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--hard&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;HEAD~2&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# oops&lt;/span&gt;
$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;reflog
16ae6a2&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;HEAD@&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;:&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;reset:&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;moving&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;to&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;HEAD~2
8e0d264&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;HEAD@&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;:&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;...
$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;reset&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--hard&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;8e0d264&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# or: git branch rescue 8e0d264&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;admonition warning&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;The safety net has an expiry date&lt;/p&gt;
&lt;p&gt;Commits that no branch, tag or reflog entry can reach are eventually deleted by &lt;code&gt;git gc&lt;/code&gt;. By default, reflog entries for unreachable commits expire after 30 days (90 days for reachable ones). Recover lost work soon, and never assume an unpushed commit is safe forever.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Integrity is built in.&lt;/strong&gt; Because every commit&#39;s hash covers its tree, and every tree&#39;s hash covers its blobs, one commit hash vouches for the entire history behind it. Change a single byte anywhere in the past and every hash after it changes. (Git is gradually moving from SHA-1 to SHA-256 for exactly this reason; you can already try it with &lt;code&gt;git init --object-format=sha256&lt;/code&gt;.)&lt;/p&gt;
&lt;h2 id=&#34;plumbing-commands-worth-knowing&#34;&gt;Plumbing commands worth knowing&lt;/h2&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git cat-file -t &amp;lt;obj&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show an object&#39;s type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git cat-file -p &amp;lt;obj&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pretty-print an object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git ls-tree -r HEAD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List every file in a commit&#39;s tree, recursively&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git rev-parse main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Turn a name (branch, tag, &lt;code&gt;HEAD~2&lt;/code&gt;…) into a hash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git hash-object -w &amp;lt;file&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Store a file as a blob&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git update-ref &amp;lt;ref&amp;gt; &amp;lt;hash&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Point a ref at a commit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git count-objects -vH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show how many objects there are and how much space they use&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&#34;recap&#34;&gt;Recap&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Git is a &lt;strong&gt;content-addressed object store&lt;/strong&gt;: every object&#39;s name is the hash of its content, so objects never change.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blobs&lt;/strong&gt; hold file contents, &lt;strong&gt;trees&lt;/strong&gt; hold directories and names, &lt;strong&gt;commits&lt;/strong&gt; point to one tree and their parents.&lt;/li&gt;
&lt;li&gt;Commits are &lt;strong&gt;snapshots&lt;/strong&gt;; unchanged files are shared between them for free.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Branches, tags and HEAD are refs&lt;/strong&gt;: small files containing a hash or another ref&#39;s name.&lt;/li&gt;
&lt;li&gt;Rebase, reset and amend &lt;strong&gt;create new objects and move pointers&lt;/strong&gt;. They don&#39;t edit history in place, which is why the &lt;strong&gt;reflog&lt;/strong&gt; can usually bring things back.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next time a Git command surprises you, ask two questions: &lt;em&gt;which objects did it create, and which pointer did it move?&lt;/em&gt;&lt;/p&gt;</content:encoded>
    </item>
    <item>
      <title>Dijkstra&#39;s Algorithm: Shortest Paths with a Priority Queue</title>
      <link>https://cryo-networks.com/en/posts/dijkstra-shortest-paths/</link>
      <guid isPermaLink="true">https://cryo-networks.com/en/posts/dijkstra-shortest-paths/</guid>
      <pubDate>Thu, 29 Oct 2026 09:00:00 +0000</pubDate>
      <category>Algorithms</category>
      <description>Implement Dijkstra&#39;s shortest-path algorithm in Python with heapq, trace it step by step, understand why it&#39;s correct, and learn exactly when it breaks: negative edges, ties in the heap and dense graphs.</description>
      <content:encoded>&lt;p&gt;Your maps app finding a route, a network router choosing where to forward a packet, a game character walking around obstacles: they&#39;re all versions of the same question. &lt;em&gt;Given a graph with weighted edges, what&#39;s the cheapest path from here to everywhere else?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For unweighted graphs, breadth-first search answers it: explore one step away, then two, then three. Weights break that. A path with three cheap edges can beat one with a single expensive edge, so &#34;fewest steps&#34; is no longer &#34;shortest&#34;. &lt;strong&gt;Dijkstra&#39;s algorithm&lt;/strong&gt;, published by Edsger Dijkstra in 1959, fixes this by always expanding the &lt;strong&gt;closest unfinished node&lt;/strong&gt; next.&lt;/p&gt;
&lt;p&gt;This article builds it in Python, traces it on a real graph, explains why it works, and covers the details that separate a textbook version from one you can trust: stale heap entries, ties, negative weights and complexity. You should be comfortable with Big-O (&lt;a href=&#34;/en/posts/big-o-notation-explained/&#34;&gt;Big-O Notation Explained with Real Code&lt;/a&gt;) and with Python dictionaries.&lt;/p&gt;
&lt;h2 id=&#34;the-example-graph&#34;&gt;The example graph&lt;/h2&gt;
&lt;p&gt;Six places joined by roads, each road labelled with its travel time. Roads go both ways.&lt;/p&gt;
&lt;figure class=&#34;diagram&#34;&gt;&lt;figcaption&gt;A graph of six nodes. Shortest distances from A: A 0, C 2, B 3, D 8, E 10, F 12. The highlighted shortest-path tree uses the edges A–C, C–B, B–D, D–E and E–F&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;We&#39;ll store it as an &lt;strong&gt;adjacency map&lt;/strong&gt;: for each node, a dictionary of neighbours and edge weights.&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;collections&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;defaultdict&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;edges&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;A&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;B&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;A&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;C&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;B&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;C&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;B&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;D&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;C&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;D&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
         &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;C&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;E&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;D&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;E&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;D&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;F&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;E&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;F&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)]&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;graph&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;defaultdict&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;dict&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;edges&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;graph&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;][&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;w&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;graph&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;][&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;w&lt;/span&gt;      &lt;span class=&#34;c1&#34;&gt;# undirected: add both directions&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;the-idea&#34;&gt;The idea&lt;/h2&gt;
&lt;p&gt;Keep a tentative distance for every node: 0 for the source, infinity for everything else. Then repeat:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Pick the unfinished node with the &lt;strong&gt;smallest&lt;/strong&gt; tentative distance. Its distance is now final.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Relax&lt;/strong&gt; each of its edges: if going through this node gives a neighbour a shorter distance than the one it has, update the neighbour.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The only hard part is step 1: finding the smallest distance quickly. Scanning every node each time costs O(V) per step. A &lt;strong&gt;priority queue&lt;/strong&gt; (a binary heap) gets it down to O(log V).&lt;/p&gt;
&lt;h2 id=&#34;the-implementation&#34;&gt;The implementation&lt;/h2&gt;
&lt;p&gt;Python&#39;s &lt;code&gt;heapq&lt;/code&gt; module turns a plain list into a min-heap. It has no &#34;decrease key&#34; operation, so when a node&#39;s distance improves we simply push a &lt;strong&gt;new&lt;/strong&gt; entry and leave the old one in the heap. When the old one eventually comes out, we recognise it as &lt;strong&gt;stale&lt;/strong&gt; and skip it. This is called &lt;em&gt;lazy deletion&lt;/em&gt;, and it&#39;s how almost every Python implementation works.&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;heapq&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;dijkstra&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;graph&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;dist&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;prev&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;                             &lt;span class=&#34;c1&#34;&gt;# prev[node] = the node we reached it from&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;heap&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)]&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;heap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;node&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;heapq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;heappop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;heap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]:&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;                      &lt;span class=&#34;c1&#34;&gt;# stale entry: we already found a shorter way here&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;neighbor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;weight&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;graph&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;new_dist&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;weight&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;new_dist&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dist&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;neighbor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;inf&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)):&lt;/span&gt;
                &lt;span class=&#34;n&#34;&gt;dist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;neighbor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;new_dist&lt;/span&gt;
                &lt;span class=&#34;n&#34;&gt;prev&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;neighbor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;node&lt;/span&gt;
                &lt;span class=&#34;n&#34;&gt;heapq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;heappush&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;heap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;new_dist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;neighbor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;prev&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;prev&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dijkstra&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;graph&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;A&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dist&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;B&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;C&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;D&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;E&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;F&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Nodes that can&#39;t be reached from the source simply don&#39;t appear in &lt;code&gt;dist&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;rebuilding-the-path&#34;&gt;Rebuilding the path&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;dist&lt;/code&gt; gives the length of each shortest path; &lt;code&gt;prev&lt;/code&gt; lets us rebuild the path itself by walking backwards from the target:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;path_to&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;prev&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;prev&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]])&lt;/span&gt;       &lt;span class=&#34;c1&#34;&gt;# KeyError means target is unreachable&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[::&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;

&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path_to&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;prev&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;A&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;F&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;C&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;B&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;D&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;E&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;F&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;The &lt;code&gt;prev&lt;/code&gt; pointers form a &lt;strong&gt;shortest-path tree&lt;/strong&gt; rooted at the source: the highlighted edges in the diagram. Notice that the best route to B isn&#39;t the direct road (4) but the detour through C (2 + 1 = 3).&lt;/p&gt;
&lt;h2 id=&#34;a-step-by-step-trace&#34;&gt;A step-by-step trace&lt;/h2&gt;
&lt;p&gt;Here&#39;s every heap operation for our graph. Watch for the &lt;strong&gt;stale&lt;/strong&gt; entries: B, D, E and F each get pushed twice because a shorter route turned up after the first push.&lt;/p&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pop&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Heap afterwards&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;(0, A)&lt;/td&gt;
&lt;td&gt;settle A; B = 4, C = 2&lt;/td&gt;
&lt;td&gt;(2, C) (4, B)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(2, C)&lt;/td&gt;
&lt;td&gt;settle C; B = 3, D = 10, E = 12&lt;/td&gt;
&lt;td&gt;(3, B) (4, B) (10, D) (12, E)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(3, B)&lt;/td&gt;
&lt;td&gt;settle B; D = 8&lt;/td&gt;
&lt;td&gt;(4, B) (8, D) (10, D) (12, E)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(4, B)&lt;/td&gt;
&lt;td&gt;stale, skip&lt;/td&gt;
&lt;td&gt;(8, D) (10, D) (12, E)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(8, D)&lt;/td&gt;
&lt;td&gt;settle D; E = 10, F = 14&lt;/td&gt;
&lt;td&gt;(10, D) (10, E) (12, E) (14, F)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(10, D)&lt;/td&gt;
&lt;td&gt;stale, skip&lt;/td&gt;
&lt;td&gt;(10, E) (12, E) (14, F)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(10, E)&lt;/td&gt;
&lt;td&gt;settle E; F = 12&lt;/td&gt;
&lt;td&gt;(12, E) (12, F) (14, F)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(12, E)&lt;/td&gt;
&lt;td&gt;stale, skip&lt;/td&gt;
&lt;td&gt;(12, F) (14, F)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(12, F)&lt;/td&gt;
&lt;td&gt;settle F&lt;/td&gt;
&lt;td&gt;(14, F)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(14, F)&lt;/td&gt;
&lt;td&gt;stale, skip&lt;/td&gt;
&lt;td&gt;empty&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&#34;why-its-correct&#34;&gt;Why it&#39;s correct&lt;/h2&gt;
&lt;p&gt;The key claim is: &lt;strong&gt;when a node is popped with its current distance &lt;code&gt;d&lt;/code&gt;, no shorter path to it exists.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Suppose a shorter path did exist. It starts at the source (already settled) and ends at our node, so somewhere along it there&#39;s a first node that isn&#39;t settled yet; call it &lt;code&gt;u&lt;/code&gt;. The part of the path before &lt;code&gt;u&lt;/code&gt; runs through settled nodes, so &lt;code&gt;u&lt;/code&gt; was relaxed with that distance and sits in the heap with a key no larger than the length of the path up to &lt;code&gt;u&lt;/code&gt;. Since every edge weight is &lt;strong&gt;non-negative&lt;/strong&gt;, the remaining edges can only add to that, so &lt;code&gt;u&lt;/code&gt;&#39;s key is at most the shorter path&#39;s length, which is less than &lt;code&gt;d&lt;/code&gt;. But then the heap would have given us &lt;code&gt;u&lt;/code&gt; before our node. Contradiction.&lt;/p&gt;
&lt;p&gt;That argument leans on exactly one assumption: &lt;strong&gt;non-negative weights&lt;/strong&gt;. Take it away and the whole thing falls apart.&lt;/p&gt;
&lt;h2 id=&#34;negative-edges-break-it&#34;&gt;Negative edges break it&lt;/h2&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;n&#34;&gt;graph&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;A&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;B&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;C&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;B&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;D&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;C&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;B&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;D&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;The true distance to B is 1 (A → C → B), and to D it&#39;s 3. But Dijkstra pops B (distance 2) before C (distance 3), so it considers B finished before the cheaper route through C has been seen. Textbook versions that mark nodes as &#34;done&#34; return B = 2 and D = 4, which is wrong.&lt;/p&gt;
&lt;p&gt;Our lazy version happens to fix itself here, because it re-pushes B when the shorter distance shows up and then processes B again. Don&#39;t rely on that: with negative edges a node can be re-processed many times, and on adversarial graphs the running time becomes exponential. If your graph can have negative weights, use the &lt;strong&gt;Bellman–Ford&lt;/strong&gt; algorithm (O(V·E)), which also detects negative cycles, where no shortest path exists at all.&lt;/p&gt;
&lt;div class=&#34;admonition note&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;Testing against Bellman–Ford&lt;/p&gt;
&lt;p&gt;Bellman–Ford is short and slow, which makes it a great reference implementation: relax every edge V − 1 times. Comparing &lt;code&gt;dijkstra()&lt;/code&gt; with it on a few thousand small random graphs with non-negative weights is a quick way to catch bugs in your implementation.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;complexity&#34;&gt;Complexity&lt;/h2&gt;
&lt;p&gt;Every node is processed only once (later entries for it are skipped as stale), so each edge is relaxed at most once in each direction and causes at most one push. The heap therefore holds O(E) entries, and every push and pop costs O(log E). Since E is at most V², log E is at most 2 log V, which gives:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Time: O((V + E) log V)&lt;/strong&gt; with a binary heap.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Space: O(V + E)&lt;/strong&gt; for the graph, the distances and the heap.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For &lt;strong&gt;dense&lt;/strong&gt; graphs, where E is close to V², a simpler O(V²) version without a heap is actually faster: keep an array of distances and scan it for the minimum each time. Theoretical structures like Fibonacci heaps reach O(E + V log V), but their overhead rarely pays off in practice.&lt;/p&gt;
&lt;h2 id=&#34;practical-details&#34;&gt;Practical details&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Stop early.&lt;/strong&gt; If you only need the path to one target, return as soon as you pop it. Its distance is final at that moment, and on a big graph you can skip most of the work:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;        &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;node&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;heapq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;heappop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;heap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;node&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Ties in the heap.&lt;/strong&gt; Heap entries are tuples, so when two distances are equal Python compares the nodes themselves. That&#39;s fine for strings and numbers, but node objects that don&#39;t support &lt;code&gt;&amp;lt;&lt;/code&gt; raise &lt;code&gt;TypeError&lt;/code&gt;. Add a counter as a tiebreaker:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;itertools&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;count&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;tiebreak&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;heapq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;heappush&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;heap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;new_dist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tiebreak&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;neighbor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Floating-point weights.&lt;/strong&gt; With float weights, two routes of &#34;equal&#34; length can differ by rounding errors. Compare with a tolerance, or scale to integers (centimetres, milliseconds), when the exact path matters.&lt;/p&gt;
&lt;h2 id=&#34;where-to-go-from-here&#34;&gt;Where to go from here&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A*&lt;/strong&gt; is Dijkstra plus a &lt;em&gt;heuristic&lt;/em&gt;: it orders the heap by &lt;code&gt;distance + estimate_to_target&lt;/code&gt;. With a good estimate (like straight-line distance on a map) it explores far fewer nodes while still finding the shortest path, as long as the estimate never overestimates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network routing&lt;/strong&gt; uses Dijkstra directly: OSPF routers compute shortest paths over the network map with it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Road networks&lt;/strong&gt; at continental scale use preprocessing techniques such as contraction hierarchies, with Dijkstra-style searches at their core.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;recap&#34;&gt;Recap&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Dijkstra finds shortest paths from one source in graphs with &lt;strong&gt;non-negative&lt;/strong&gt; edge weights.&lt;/li&gt;
&lt;li&gt;Always expand the &lt;strong&gt;closest unfinished node&lt;/strong&gt;; a min-heap (&lt;code&gt;heapq&lt;/code&gt;) finds it in O(log V).&lt;/li&gt;
&lt;li&gt;Without decrease-key, &lt;strong&gt;push duplicates and skip stale entries&lt;/strong&gt; (&lt;code&gt;if d &amp;gt; dist[node]: continue&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Keep &lt;strong&gt;&lt;code&gt;prev&lt;/code&gt; pointers&lt;/strong&gt; to rebuild paths; they form a shortest-path tree.&lt;/li&gt;
&lt;li&gt;Runs in &lt;strong&gt;O((V + E) log V)&lt;/strong&gt;; use &lt;strong&gt;Bellman–Ford&lt;/strong&gt; for negative edges and an O(V²) array scan for very dense graphs.&lt;/li&gt;
&lt;/ul&gt;</content:encoded>
    </item>
    <item>
      <title>CSS Stacking Contexts: Why z-index: 9999 Doesn&#39;t Work</title>
      <link>https://cryo-networks.com/en/posts/css-stacking-contexts/</link>
      <guid isPermaLink="true">https://cryo-networks.com/en/posts/css-stacking-contexts/</guid>
      <pubDate>Thu, 29 Oct 2026 09:00:00 +0000</pubDate>
      <category>CSS</category>
      <description>z-index isn&#39;t a global ranking. It only compares elements inside the same stacking context, and a dozen properties quietly create new ones. Learn the painting order, find the context that traps your element, and fix overlap bugs for good.</description>
      <content:encoded>&lt;p&gt;It&#39;s one of the most common CSS bugs there is. The modal has &lt;code&gt;z-index: 9999&lt;/code&gt;. The site header has &lt;code&gt;z-index: 10&lt;/code&gt;. And the header is drawn &lt;strong&gt;on top of&lt;/strong&gt; the modal. You try 99999, then &lt;code&gt;!important&lt;/code&gt;; nothing changes.&lt;/p&gt;
&lt;p&gt;The browser isn&#39;t broken. &lt;code&gt;z-index&lt;/code&gt; is not a global ranking of every element on the page. It only orders elements &lt;strong&gt;within the same stacking context&lt;/strong&gt;, and your modal is trapped inside a context that, as a whole, sits below the header. This article explains how the browser decides what&#39;s on top, what creates those contexts (often by accident), and how to find and fix the one causing your bug. It assumes you&#39;re comfortable with the cascade (&lt;a href=&#34;/en/posts/css-cascade-specificity/&#34;&gt;The CSS Cascade and Specificity&lt;/a&gt;) and with positioning.&lt;/p&gt;
&lt;h2 id=&#34;the-bug&#34;&gt;The bug&lt;/h2&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;header&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;site-header&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;…&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;header&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;main&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;page&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;modal&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;…&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;site-header&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;position&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;sticky&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;top&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;z-index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;position&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;relative&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;z-index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;modal&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;position&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;fixed&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;inset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;z-index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;9999&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;The modal is drawn underneath the header. To see why, we need two ideas: the &lt;strong&gt;painting order&lt;/strong&gt; inside one stacking context, and how contexts &lt;strong&gt;nest&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;painting-order-inside-a-stacking-context&#34;&gt;Painting order inside a stacking context&lt;/h2&gt;
&lt;p&gt;A stacking context is a group of elements that the browser paints together, as a unit. Inside one, it paints in this order, from bottom to top:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;strong&gt;background and borders&lt;/strong&gt; of the element that forms the context&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Positioned&lt;/strong&gt; descendants with a &lt;strong&gt;negative&lt;/strong&gt; &lt;code&gt;z-index&lt;/code&gt; (most negative first)&lt;/li&gt;
&lt;li&gt;Normal &lt;strong&gt;block&lt;/strong&gt; boxes, in document order&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Floats&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inline&lt;/strong&gt; content: text, images, inline-blocks&lt;/li&gt;
&lt;li&gt;Positioned descendants with &lt;code&gt;z-index: auto&lt;/code&gt; or &lt;code&gt;0&lt;/code&gt;, in document order&lt;/li&gt;
&lt;li&gt;Positioned descendants with a &lt;strong&gt;positive&lt;/strong&gt; &lt;code&gt;z-index&lt;/code&gt;, lowest first&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Two consequences surprise people. First, &lt;strong&gt;positioned elements paint above non-positioned ones&lt;/strong&gt; even without any &lt;code&gt;z-index&lt;/code&gt;: adding &lt;code&gt;position: relative&lt;/code&gt; alone can bring an element to the front. Second, &lt;code&gt;z-index&lt;/code&gt; only has an effect on &lt;strong&gt;positioned elements&lt;/strong&gt; (&lt;code&gt;relative&lt;/code&gt;, &lt;code&gt;absolute&lt;/code&gt;, &lt;code&gt;fixed&lt;/code&gt;, &lt;code&gt;sticky&lt;/code&gt;) and on &lt;strong&gt;flex and grid items&lt;/strong&gt;. On a plain static block it&#39;s ignored.&lt;/p&gt;
&lt;h2 id=&#34;contexts-nest-like-version-numbers&#34;&gt;Contexts nest like version numbers&lt;/h2&gt;
&lt;p&gt;Here&#39;s the crucial rule: &lt;strong&gt;a stacking context is painted as a single layer inside its parent context&lt;/strong&gt;, at the position given by its own &lt;code&gt;z-index&lt;/code&gt;. Everything inside it is drawn within that layer, so a child can never escape its parent&#39;s position, however high its own &lt;code&gt;z-index&lt;/code&gt; is.&lt;/p&gt;
&lt;p&gt;A good mental model is version numbers. In our example, the root context contains two layers, the header (10) and &lt;code&gt;.page&lt;/code&gt; (1). The modal&#39;s 9999 only matters inside &lt;code&gt;.page&lt;/code&gt;, so its effective position is something like &lt;strong&gt;1.9999&lt;/strong&gt;:&lt;/p&gt;
&lt;figure class=&#34;diagram&#34;&gt;&lt;figcaption&gt;The root stacking context contains the header at z-index 10 and .page at z-index 1. The modal&#39;s z-index 9999 only applies inside .page, so its effective order is 1.9999, which is below 10&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;The browser first compares 10 with 1 at the root level, and the header wins. The 9999 is never compared with the header at all. That&#39;s the whole bug.&lt;/p&gt;
&lt;h2 id=&#34;what-creates-a-stacking-context&#34;&gt;What creates a stacking context&lt;/h2&gt;
&lt;p&gt;This is the list that matters, because most stacking contexts are created &lt;strong&gt;by accident&lt;/strong&gt;, as a side effect of a property added for a different reason:&lt;/p&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Creates a context when…&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;position: relative&lt;/code&gt; / &lt;code&gt;absolute&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;z-index&lt;/code&gt; is anything other than &lt;code&gt;auto&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;position: fixed&lt;/code&gt; / &lt;code&gt;sticky&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;always&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flex or grid &lt;strong&gt;item&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;z-index&lt;/code&gt; is anything other than &lt;code&gt;auto&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;opacity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;less than &lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;transform&lt;/code&gt;, &lt;code&gt;scale&lt;/code&gt;, &lt;code&gt;rotate&lt;/code&gt;, &lt;code&gt;translate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;anything other than &lt;code&gt;none&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;backdrop-filter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;anything other than &lt;code&gt;none&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;clip-path&lt;/code&gt;, &lt;code&gt;mask&lt;/code&gt;, &lt;code&gt;perspective&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;anything other than &lt;code&gt;none&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mix-blend-mode&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;anything other than &lt;code&gt;normal&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;isolation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;isolate&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;will-change&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;names one of the properties above&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;contain&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;layout&lt;/code&gt;, &lt;code&gt;paint&lt;/code&gt;, &lt;code&gt;strict&lt;/code&gt; or &lt;code&gt;content&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;container-type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;size&lt;/code&gt; or &lt;code&gt;inline-size&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Plus the root element (&lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;), which is always one.&lt;/p&gt;
&lt;p&gt;So a fade-in animation (&lt;code&gt;opacity: 0.99&lt;/code&gt; during the transition), a &lt;code&gt;transform: translateZ(0)&lt;/code&gt; &#34;performance hack&#34;, or a &lt;code&gt;will-change: transform&lt;/code&gt; on a wrapper can each trap every &lt;code&gt;z-index&lt;/code&gt; inside it. Classic real-world causes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An &lt;strong&gt;animated page wrapper&lt;/strong&gt; with &lt;code&gt;transform&lt;/code&gt;, which traps every modal and dropdown inside it.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;sticky header&lt;/strong&gt; in one part of the page and a dropdown inside a different sticky section.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;card with &lt;code&gt;opacity: 0.95&lt;/code&gt;&lt;/strong&gt; whose tooltip slides under the next card.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;admonition warning&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;transform also breaks position: fixed&lt;/p&gt;
&lt;p&gt;An ancestor with &lt;code&gt;transform&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;perspective&lt;/code&gt; or &lt;code&gt;will-change: transform&lt;/code&gt; doesn&#39;t just create a stacking context: it also becomes the &lt;strong&gt;containing block&lt;/strong&gt; for &lt;code&gt;position: fixed&lt;/code&gt; descendants. Your &#34;fixed&#34; modal then positions itself relative to that ancestor instead of the viewport, and scrolls with it. If a fixed element is in the wrong place &lt;em&gt;and&lt;/em&gt; in the wrong layer, look for a transform.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;finding-the-context-that-traps-you&#34;&gt;Finding the context that traps you&lt;/h2&gt;
&lt;p&gt;DevTools don&#39;t show stacking contexts directly, but you can find them with a few lines of JavaScript. Select the misbehaving element in the Elements panel, then paste this into the console (&lt;code&gt;$0&lt;/code&gt; is the selected element):&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stackingContexts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;el&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;found&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;el&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parentElement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parentElement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getComputedStyle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parent&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parentElement&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getComputedStyle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parentElement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;reasons&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;flexOrGridItem&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;sr&#34;&gt;/flex|grid/&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;display&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;??&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;zIndex&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!==&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;auto&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;position&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!==&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;static&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;flexOrGridItem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;reasons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`z-index: &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;zIndex&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;position&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;===&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;fixed&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;position&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;===&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;sticky&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;reasons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`position: &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;position&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;opacity&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!==&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;1&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;reasons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`opacity: &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;opacity&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prop&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;of&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;transform&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;scale&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;rotate&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;translate&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;filter&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;backdropFilter&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;perspective&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;clipPath&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!==&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;none&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;reasons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mixBlendMode&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!==&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;normal&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;reasons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`mix-blend-mode: &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mixBlendMode&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;isolation&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;===&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;isolate&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;reasons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;isolation: isolate&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sr&#34;&gt;/transform|opacity|filter/&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;willChange&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;reasons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`will-change: &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;willChange&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;reasons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;found&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;element&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;reasons&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;reasons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;join&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;found&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;table&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stackingContexts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;$0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;It walks up from the element and lists every ancestor that forms a stacking context, with the reason. It covers the common causes rather than every rule in the table. The &lt;strong&gt;first&lt;/strong&gt; row is the context your &lt;code&gt;z-index&lt;/code&gt; is trapped in; compare the &lt;code&gt;z-index&lt;/code&gt; of the element you&#39;re fighting with against &lt;em&gt;that&lt;/em&gt; ancestor&#39;s, not against your own.&lt;/p&gt;
&lt;h2 id=&#34;fixing-it&#34;&gt;Fixing it&lt;/h2&gt;
&lt;p&gt;Once you know which context traps the element, pick the fix that matches the cause:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Remove the accidental context.&lt;/strong&gt; If &lt;code&gt;.page&lt;/code&gt; doesn&#39;t actually need &lt;code&gt;z-index: 1&lt;/code&gt; (or that &lt;code&gt;transform&lt;/code&gt;, or &lt;code&gt;will-change&lt;/code&gt;), delete it. This is the best fix when the property was added &#34;just in case&#34;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Move the element out.&lt;/strong&gt; Overlays like modals, toasts and dropdown menus are best rendered as direct children of &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;, outside every component&#39;s context. Most UI frameworks have a tool for it: React&#39;s &lt;code&gt;createPortal&lt;/code&gt;, Vue&#39;s &lt;code&gt;&amp;lt;Teleport&amp;gt;&lt;/code&gt;, Svelte&#39;s portal libraries.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Use the top layer.&lt;/strong&gt; Modern browsers have a &lt;strong&gt;top layer&lt;/strong&gt; that sits above the entire document, outside all stacking contexts. Elements get there through the platform&#39;s own APIs, so no &lt;code&gt;z-index&lt;/code&gt; is needed at all:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;dialog&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;confirm&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Delete this file?&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;form&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;dialog&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;button&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Cancel&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;button&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;button&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;ok&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Delete&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;button&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;form&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;dialog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;script&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;document&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getElementById&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;confirm&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;showModal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// rendered in the top layer&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;script&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; opened with &lt;code&gt;showModal()&lt;/code&gt;, elements with the &lt;code&gt;popover&lt;/code&gt; attribute, and fullscreen elements all render in the top layer, wherever they are in the DOM. &lt;code&gt;showModal()&lt;/code&gt; also adds a &lt;code&gt;::backdrop&lt;/code&gt;, traps focus and closes on &lt;kbd&gt;Esc&lt;/kbd&gt;, which are things a hand-made modal usually gets wrong.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Raise the whole context.&lt;/strong&gt; If neither of the above works, change the &lt;code&gt;z-index&lt;/code&gt; of the &lt;strong&gt;context root&lt;/strong&gt; (&lt;code&gt;.page&lt;/code&gt;), not the element inside it. Remember this moves everything inside &lt;code&gt;.page&lt;/code&gt; along with it.&lt;/p&gt;
&lt;h2 id=&#34;isolation-isolate-the-useful-kind-of-context&#34;&gt;isolation: isolate, the useful kind of context&lt;/h2&gt;
&lt;p&gt;Sometimes you &lt;em&gt;want&lt;/em&gt; a stacking context. A common example is a decorative layer behind a card&#39;s content:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;card&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;position&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;relative&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;background&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;white&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;card&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;before&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;position&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;absolute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;inset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;-4&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;px&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;z-index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;-1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;                 &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;/* behind the card&amp;#39;s content... */&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;background&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;linear-gradient&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;135&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;deg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;violet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;orange&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;border-radius&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;inherit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Without a stacking context on &lt;code&gt;.card&lt;/code&gt;, the &lt;code&gt;::before&lt;/code&gt; with &lt;code&gt;z-index: -1&lt;/code&gt; is painted in whatever context the card lives in, at step 2 of the painting order: &lt;strong&gt;below the card&#39;s own white background&lt;/strong&gt;. The glow is hidden behind the card it was meant to frame, and could even slide behind the backgrounds of the card&#39;s ancestors.&lt;/p&gt;
&lt;p&gt;Give the card its own context and the problem disappears:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;card&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;position&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;relative&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;background&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;white&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;isolation&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;isolate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Now the &lt;code&gt;::before&lt;/code&gt; is painted inside the card&#39;s context: above the card&#39;s background, below its content. &lt;code&gt;isolation: isolate&lt;/code&gt; exists for exactly this. It creates a context and does nothing else: no positioning, no &lt;code&gt;z-index&lt;/code&gt; value to manage, no side effects.&lt;/p&gt;
&lt;div class=&#34;admonition tip&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;A z-index scale for the rest&lt;/p&gt;
&lt;p&gt;For elements that really do share a context, avoid an arms race of ever-bigger numbers. Define a small scale once, for example &lt;code&gt;--z-dropdown: 10; --z-sticky: 20; --z-overlay: 30; --z-toast: 40;&lt;/code&gt;, and only use those values. If something needs to go between two levels, that&#39;s a design decision, not a number to make up.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;recap&#34;&gt;Recap&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;z-index&lt;/code&gt; only orders elements &lt;strong&gt;inside the same stacking context&lt;/strong&gt;; contexts nest, so think in version numbers like &lt;strong&gt;1.9999 &amp;lt; 10&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Within a context the painting order is: background, negative &lt;code&gt;z-index&lt;/code&gt;, blocks, floats, inline content, &lt;code&gt;z-index: auto&lt;/code&gt;/&lt;code&gt;0&lt;/code&gt;, positive &lt;code&gt;z-index&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;z-index&lt;/code&gt; only works on &lt;strong&gt;positioned elements and flex/grid items&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Many properties create contexts as a side effect: &lt;strong&gt;&lt;code&gt;opacity&lt;/code&gt; &amp;lt; 1, &lt;code&gt;transform&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;will-change&lt;/code&gt;, fixed/sticky positioning&lt;/strong&gt; and more.&lt;/li&gt;
&lt;li&gt;Fix overlap bugs by removing accidental contexts, moving overlays to &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;, or using the &lt;strong&gt;top layer&lt;/strong&gt; (&lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;, &lt;code&gt;popover&lt;/code&gt;). Use &lt;strong&gt;&lt;code&gt;isolation: isolate&lt;/code&gt;&lt;/strong&gt; when you want a context on purpose.&lt;/li&gt;
&lt;/ul&gt;</content:encoded>
    </item>
    <item>
      <title>Binary Search Without Off-by-One Bugs</title>
      <link>https://cryo-networks.com/en/posts/binary-search-boundaries/</link>
      <guid isPermaLink="true">https://cryo-networks.com/en/posts/binary-search-boundaries/</guid>
      <pubDate>Thu, 29 Oct 2026 09:00:00 +0000</pubDate>
      <category>Algorithms</category>
      <description>Binary search is easy to explain and surprisingly easy to get wrong. Learn one loop invariant that makes it correct every time, find first and last positions, and use binary search on answers instead of arrays.</description>
      <content:encoded>&lt;p&gt;Binary search fits in one sentence: &lt;em&gt;look at the middle item, then throw away the half that can&#39;t contain what you&#39;re looking for.&lt;/em&gt; Each step halves the remaining range, so even a billion sorted items need only about 30 steps. That&#39;s &lt;strong&gt;O(log n)&lt;/strong&gt;; if Big-O is new to you, start with &lt;a href=&#34;/en/posts/big-o-notation-explained/&#34;&gt;Big-O Notation Explained with Real Code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Writing it correctly is another story. In &lt;em&gt;Programming Pearls&lt;/em&gt;, Jon Bentley reports that when he asked professional programmers to write binary search, about 90% of them had bugs. And the version in Java&#39;s standard library had an overflow bug that went unnoticed for about nine years. The usual culprits are off-by-one errors: &lt;code&gt;&amp;lt;&lt;/code&gt; or &lt;code&gt;&amp;lt;=&lt;/code&gt;? &lt;code&gt;mid&lt;/code&gt; or &lt;code&gt;mid + 1&lt;/code&gt;? When does the loop stop?&lt;/p&gt;
&lt;p&gt;This article gives you one template that answers those questions for you, and shows how far it stretches.&lt;/p&gt;
&lt;h2 id=&#34;the-question-to-ask&#34;&gt;The question to ask&lt;/h2&gt;
&lt;p&gt;&#34;Is &lt;code&gt;x&lt;/code&gt; in the list?&#34; is the weakest form of binary search. A much more useful question is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Where is the first item that is greater than or equal to &lt;code&gt;x&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is usually called &lt;strong&gt;lower bound&lt;/strong&gt;. It answers several questions at once: whether &lt;code&gt;x&lt;/code&gt; is present, where it starts if it appears several times, and where you&#39;d insert it to keep the list sorted.&lt;/p&gt;
&lt;h2 id=&#34;one-template-a-half-open-range&#34;&gt;One template: a half-open range&lt;/h2&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;lower_bound&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;&amp;quot;&amp;quot;&amp;quot;Index of the first item &amp;gt;= x, or len(a) if every item is smaller.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;lo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hi&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;            &lt;span class=&#34;c1&#34;&gt;# invariant: the answer is between lo and hi (inclusive)&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;mid&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;//&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;lo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mid&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;          &lt;span class=&#34;c1&#34;&gt;# a[mid] is too small, so the answer is to its right&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;hi&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mid&lt;/span&gt;              &lt;span class=&#34;c1&#34;&gt;# a[mid] could be the answer: keep it in range&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lo&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Every line follows from the &lt;strong&gt;invariant&lt;/strong&gt; in the comment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;hi&lt;/code&gt; starts at &lt;code&gt;len(a)&lt;/code&gt;, not &lt;code&gt;len(a) - 1&lt;/code&gt;, because &#34;no item is big enough&#34; is a valid answer.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;a[mid] &amp;lt; x&lt;/code&gt;, then &lt;code&gt;mid&lt;/code&gt; and everything before it are too small, so the answer is at least &lt;code&gt;mid + 1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Otherwise &lt;code&gt;a[mid] &amp;gt;= x&lt;/code&gt;, so &lt;code&gt;mid&lt;/code&gt; &lt;em&gt;might&lt;/em&gt; be the answer. We can&#39;t skip it, so &lt;code&gt;hi = mid&lt;/code&gt;, not &lt;code&gt;mid - 1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The range shrinks on every pass (&lt;code&gt;mid&lt;/code&gt; is always less than &lt;code&gt;hi&lt;/code&gt;), so the loop ends. When &lt;code&gt;lo == hi&lt;/code&gt;, there&#39;s only one candidate left, and that&#39;s the answer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&#39;s trace a search for &lt;code&gt;23&lt;/code&gt;:&lt;/p&gt;
&lt;figure class=&#34;diagram&#34;&gt;&lt;figcaption&gt;lower_bound searching for 23: the range starts as the whole list, then shrinks to indices 0–5, 3–5, 3–4, and finally index 4, the first 23&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;23&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;23&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;31&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;42&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;56&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;70&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;lower_bound&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;23&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;# 4  (the first 23)&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;lower_bound&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;# 4  (20 would be inserted before the first 23)&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;lower_bound&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;99&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;# 10 (bigger than everything: len(a))&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;lower_bound&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;# 0&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Checking membership is now a one-liner, with no special cases inside the loop:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;contains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lower_bound&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;upper-bound-and-counting&#34;&gt;Upper bound and counting&lt;/h2&gt;
&lt;p&gt;Change &lt;code&gt;&amp;lt;&lt;/code&gt; to &lt;code&gt;&amp;lt;=&lt;/code&gt; and you get &lt;strong&gt;upper bound&lt;/strong&gt;: the first item strictly greater than &lt;code&gt;x&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;upper_bound&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;lo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hi&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;mid&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;//&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;lo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mid&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;hi&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mid&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lo&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Together they give you the whole block of equal items, so counting duplicates in a sorted list takes O(log n), not O(n):&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;n&#34;&gt;upper_bound&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;23&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lower_bound&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;23&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;# 2&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lower_bound&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;23&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;upper_bound&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;23&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)]&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# [23, 23]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;admonition tip&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;Python already has these&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;bisect&lt;/code&gt; module is exactly this: &lt;code&gt;bisect.bisect_left(a, x)&lt;/code&gt; is &lt;code&gt;lower_bound&lt;/code&gt; and &lt;code&gt;bisect.bisect_right(a, x)&lt;/code&gt; is &lt;code&gt;upper_bound&lt;/code&gt;. &lt;code&gt;bisect.insort(a, x)&lt;/code&gt; inserts while keeping the list sorted. Since Python 3.10 they accept &lt;code&gt;key=&lt;/code&gt;, e.g. &lt;code&gt;bisect_left(users, 35, key=lambda u: u.age)&lt;/code&gt;. Use them in real code; write your own when you need the general version below.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;the-general-version-first-true&#34;&gt;The general version: first True&lt;/h2&gt;
&lt;p&gt;Look at the two functions again. All that changes is a test that is &lt;code&gt;False&lt;/code&gt; for a while and then &lt;code&gt;True&lt;/code&gt; from some point on:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;index:      0      1      2      3      4     5     6     ...
a[i] &amp;gt;= 23: False  False  False  False  True  True  True  ...
                                        ↑ the answer&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;So binary search really finds &lt;strong&gt;the boundary of a yes/no question&lt;/strong&gt;, as long as the answers never flip back from &lt;code&gt;True&lt;/code&gt; to &lt;code&gt;False&lt;/code&gt;. That&#39;s worth its own function:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;first_true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pred&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;&amp;quot;&amp;quot;&amp;quot;Smallest i in [lo, hi) where pred(i) is True, or hi if there is none.&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;    pred must be False, False, ..., True, True over that range.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;mid&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;//&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pred&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;hi&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mid&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;lo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mid&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lo&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;lower_bound(a, x)&lt;/code&gt; is just &lt;code&gt;first_true(0, len(a), lambda i: a[i] &amp;gt;= x)&lt;/code&gt;. But the predicate doesn&#39;t need an array at all.&lt;/p&gt;
&lt;h2 id=&#34;binary-search-on-the-answer&#34;&gt;Binary search on the answer&lt;/h2&gt;
&lt;p&gt;The integer square root of &lt;code&gt;n&lt;/code&gt; is the largest &lt;code&gt;r&lt;/code&gt; with &lt;code&gt;r * r &amp;lt;= n&lt;/code&gt;. In other words, it&#39;s one less than the first &lt;code&gt;r&lt;/code&gt; where &lt;code&gt;r * r &amp;gt; n&lt;/code&gt;:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;isqrt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;first_true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;lambda&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;r&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;r&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;isqrt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;50&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;          &lt;span class=&#34;c1&#34;&gt;# 7&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;isqrt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;      &lt;span class=&#34;c1&#34;&gt;# 1000000, in about 40 steps&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;This trick solves a whole family of &#34;find the minimum value that works&#34; problems. Say a truck must deliver packages &lt;strong&gt;in order&lt;/strong&gt;, the truck can carry &lt;code&gt;capacity&lt;/code&gt; kilograms per day, and everything has to be delivered within &lt;code&gt;days&lt;/code&gt; days. What&#39;s the smallest capacity that works?&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;days_needed&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;weights&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;capacity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;days&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;load&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;weights&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;load&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;capacity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;      &lt;span class=&#34;c1&#34;&gt;# doesn&amp;#39;t fit: start a new day&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;days&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;load&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;days&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;load&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;w&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;days&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;min_capacity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;weights&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;days&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;lo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hi&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;weights&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;sum&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;weights&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;first_true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;lambda&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;days_needed&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;weights&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;days&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;min_capacity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;days&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;# 6: [3, 2] [2, 4] [1, 4]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Finding the capacity directly is hard, but &lt;em&gt;checking&lt;/em&gt; one capacity is easy, and the answer is monotonic: if capacity 6 works, 7 works too. Binary search turns an easy check into a fast search: O(n log S) instead of trying every capacity up to &lt;code&gt;S = sum(weights)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Some boundary details, all following from the template: the capacity can&#39;t be below the heaviest package, so &lt;code&gt;lo = max(weights)&lt;/code&gt;. We pass &lt;code&gt;hi = sum(weights)&lt;/code&gt; even though &lt;code&gt;first_true&lt;/code&gt; never tests &lt;code&gt;hi&lt;/code&gt; itself: if no smaller capacity works, it returns &lt;code&gt;hi&lt;/code&gt;, which is correct because carrying everything in one day always works.&lt;/p&gt;
&lt;div class=&#34;admonition note&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;You already know one: git bisect&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git bisect&lt;/code&gt; is this exact algorithm over commits: the question is &#34;is the bug present in this commit?&#34;, and the answers go from no to yes at exactly one point. A thousand commits need only about 10 tests.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;the-bugs-to-watch-for&#34;&gt;The bugs to watch for&lt;/h2&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bug&lt;/th&gt;
&lt;th&gt;What goes wrong&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hi = len(a) - 1&lt;/code&gt; with &lt;code&gt;while lo &amp;lt; hi&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;len(a)&lt;/code&gt; can never be returned, and the last item is never checked&lt;/td&gt;
&lt;td&gt;Pick one convention (we use half-open &lt;code&gt;[lo, hi)&lt;/code&gt;) and stick to it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lo = mid&lt;/code&gt; instead of &lt;code&gt;mid + 1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When &lt;code&gt;hi = lo + 1&lt;/code&gt;, &lt;code&gt;mid == lo&lt;/code&gt; forever: an infinite loop&lt;/td&gt;
&lt;td&gt;Each branch must shrink the range&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hi = mid - 1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Can skip over the answer when &lt;code&gt;a[mid]&lt;/code&gt; &lt;em&gt;is&lt;/em&gt; the answer&lt;/td&gt;
&lt;td&gt;Only exclude &lt;code&gt;mid&lt;/code&gt; when it definitely isn&#39;t the answer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;(lo + hi) / 2&lt;/code&gt; in Java, C, Go…&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lo + hi&lt;/code&gt; can overflow a 32-bit int on huge arrays&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;lo + (hi - lo) / 2&lt;/code&gt; (Python&#39;s integers can&#39;t overflow)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Searching unsorted data&lt;/td&gt;
&lt;td&gt;Returns a plausible but wrong answer, silently&lt;/td&gt;
&lt;td&gt;Sort first, or check the predicate really is monotonic&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&#34;test-it-against-the-slow-version&#34;&gt;Test it against the slow version&lt;/h2&gt;
&lt;p&gt;Binary search is the perfect candidate for a &lt;strong&gt;brute-force comparison test&lt;/strong&gt;: a linear scan is obviously correct, so compare against it on thousands of random inputs, including empty lists and duplicates:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;random&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10_000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;sorted&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;randint&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;randint&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)))&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;randint&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;22&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;expected&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;enumerate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;assert&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lower_bound&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;expected&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Small values (0–20) force lots of duplicates and edge cases. This kind of test catches every bug in the table above within a fraction of a second.&lt;/p&gt;
&lt;h2 id=&#34;recap&#34;&gt;Recap&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Ask &lt;strong&gt;&#34;where is the first item that is ≥ x?&#34;&lt;/strong&gt; (lower bound) rather than &#34;is x here?&#34;.&lt;/li&gt;
&lt;li&gt;Use a &lt;strong&gt;half-open range&lt;/strong&gt; &lt;code&gt;[lo, hi)&lt;/code&gt; with &lt;code&gt;while lo &amp;lt; hi&lt;/code&gt;, &lt;code&gt;lo = mid + 1&lt;/code&gt; and &lt;code&gt;hi = mid&lt;/code&gt;. Every step shrinks the range, and &lt;code&gt;lo&lt;/code&gt; is the answer at the end.&lt;/li&gt;
&lt;li&gt;Binary search finds the &lt;strong&gt;boundary of any monotonic yes/no question&lt;/strong&gt;, so you can search over answers (capacities, sizes, times, commits), not just arrays.&lt;/li&gt;
&lt;li&gt;In Python, reach for &lt;code&gt;bisect&lt;/code&gt; first.&lt;/li&gt;
&lt;li&gt;Test against a brute-force version with small random inputs.&lt;/li&gt;
&lt;/ul&gt;</content:encoded>
    </item>
    <item>
      <title>Python List Comprehensions: From Loops to Elegant One-Liners</title>
      <link>https://cryo-networks.com/en/posts/python-list-comprehensions/</link>
      <guid isPermaLink="true">https://cryo-networks.com/en/posts/python-list-comprehensions/</guid>
      <pubDate>Mon, 26 Oct 2026 09:00:00 +0000</pubDate>
      <category>Python</category>
      <description>Learn how list comprehensions work, when they make your code clearer, and when a plain for-loop is still the better choice.</description>
      <content:encoded>&lt;p&gt;If you&#39;ve read other people&#39;s Python code, you&#39;ve almost certainly seen something like this:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;n&#34;&gt;squares&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;That&#39;s a &lt;strong&gt;list comprehension&lt;/strong&gt;: a compact way to build a new list from an existing iterable. Once it clicks, you&#39;ll use it constantly. In this guide we&#39;ll build up from the plain &lt;code&gt;for&lt;/code&gt; loop you already know, cover filtering and transformation, look at dictionary and set comprehensions, and finish with the cases where you &lt;em&gt;shouldn&#39;t&lt;/em&gt; use one.&lt;/p&gt;
&lt;h2 id=&#34;start-with-the-loop-you-already-know&#34;&gt;Start with the loop you already know&lt;/h2&gt;
&lt;p&gt;Here&#39;s the classic way to build a list of squares:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;n&#34;&gt;squares&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;squares&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;squares&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Three lines, three jobs: create an empty list, loop, append. A comprehension expresses the same idea in one line:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;n&#34;&gt;squares&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Read it left to right as &lt;em&gt;&#34;give me &lt;code&gt;n * n&lt;/code&gt; for each &lt;code&gt;n&lt;/code&gt; in &lt;code&gt;range(10)&lt;/code&gt;.&#34;&lt;/em&gt; The expression you want to collect comes first, followed by the loop that produces the values.&lt;/p&gt;
&lt;h2 id=&#34;the-anatomy-of-a-comprehension&#34;&gt;The anatomy of a comprehension&lt;/h2&gt;
&lt;p&gt;Every list comprehension follows this shape:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;[expression for item in iterable if condition]&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;diagram&#34;&gt;&lt;figcaption&gt;The same three parts in a real comprehension that squares the even numbers below 10&lt;/figcaption&gt;&lt;/figure&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Required?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;expression&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;What to put in the new list&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;for item in iterable&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Where the values come from&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;if condition&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keep only items that pass the test&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&#34;filtering-with-if&#34;&gt;Filtering with &lt;code&gt;if&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Add a condition at the end to keep only some items. Here we keep the even numbers:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;n&#34;&gt;evens&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;You can transform and filter at the same time. This collects the lengths of words longer than three characters:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;n&#34;&gt;words&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;git&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;python&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;sql&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;linux&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;css&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;docker&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;long_word_lengths&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;words&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;# [6, 5, 6]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;choosing-a-value-with-if-else&#34;&gt;Choosing a value with &lt;code&gt;if ... else&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;What if you want to keep &lt;em&gt;every&lt;/em&gt; item but change some of them? Put an &lt;code&gt;if ... else&lt;/code&gt; &lt;strong&gt;before&lt;/strong&gt; the &lt;code&gt;for&lt;/code&gt;. This is Python&#39;s conditional expression, not a filter:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;n&#34;&gt;scores&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;92&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;47&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;78&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;30&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;85&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;results&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;pass&amp;quot;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;50&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;fail&amp;quot;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scores&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;# [&amp;#39;pass&amp;#39;, &amp;#39;fail&amp;#39;, &amp;#39;pass&amp;#39;, &amp;#39;fail&amp;#39;, &amp;#39;pass&amp;#39;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;admonition tip&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;Remember the position rule&lt;/p&gt;
&lt;p&gt;An &lt;code&gt;if&lt;/code&gt; &lt;strong&gt;after&lt;/strong&gt; the &lt;code&gt;for&lt;/code&gt; filters items out. An &lt;code&gt;if ... else&lt;/code&gt; &lt;strong&gt;before&lt;/strong&gt; the &lt;code&gt;for&lt;/code&gt; chooses what each item becomes. The first can shrink your list; the second never does.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;real-world-examples&#34;&gt;Real-world examples&lt;/h2&gt;
&lt;p&gt;Comprehensions shine when you&#39;re cleaning or reshaping data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Normalising user input:&lt;/strong&gt;&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;n&#34;&gt;raw_emails&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;  Ada@Example.com&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;grace@example.com  &amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;LINUS@example.com&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;emails&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lower&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;raw_emails&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()]&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;# [&amp;#39;ada@example.com&amp;#39;, &amp;#39;grace@example.com&amp;#39;, &amp;#39;linus@example.com&amp;#39;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Extracting a field from a list of dictionaries:&lt;/strong&gt;&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;n&#34;&gt;users&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;Ada&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;active&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;Grace&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;active&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;Linus&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;active&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;active_names&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;u&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;users&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;active&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]]&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;# [&amp;#39;Ada&amp;#39;, &amp;#39;Linus&amp;#39;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Flattening a nested list.&lt;/strong&gt; The loops appear in the same order as they would in nested &lt;code&gt;for&lt;/code&gt; statements:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;n&#34;&gt;matrix&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;9&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]]&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;flat&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;row&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;matrix&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;row&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;# [1, 2, 3, 4, 5, 6, 7, 8, 9]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;dictionary-and-set-comprehensions&#34;&gt;Dictionary and set comprehensions&lt;/h2&gt;
&lt;p&gt;The same syntax works with curly braces. Use &lt;code&gt;key: value&lt;/code&gt; to build a dictionary:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;n&#34;&gt;words&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;git&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;python&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;sql&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;lengths&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;words&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;# {&amp;#39;git&amp;#39;: 3, &amp;#39;python&amp;#39;: 6, &amp;#39;sql&amp;#39;: 3}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Drop the colon and you get a set, which removes duplicates automatically:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;n&#34;&gt;unique_lengths&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;words&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;# {3, 6}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;generator-expressions-comprehensions-without-the-list&#34;&gt;Generator expressions: comprehensions without the list&lt;/h2&gt;
&lt;p&gt;Swap the square brackets for parentheses and you get a &lt;strong&gt;generator expression&lt;/strong&gt;. It produces values one at a time instead of building the whole list in memory, which is perfect when you only need to consume the values once:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;n&#34;&gt;total&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;sum&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1_000_000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;When a generator expression is the only argument to a function, you can drop the extra parentheses, as above. Functions like &lt;code&gt;sum()&lt;/code&gt;, &lt;code&gt;any()&lt;/code&gt;, &lt;code&gt;all()&lt;/code&gt;, &lt;code&gt;max()&lt;/code&gt; and &lt;code&gt;min()&lt;/code&gt; pair beautifully with them:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;n&#34;&gt;has_admin&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;any&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;role&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;admin&amp;quot;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;u&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;users&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;when-not-to-use-a-comprehension&#34;&gt;When &lt;em&gt;not&lt;/em&gt; to use a comprehension&lt;/h2&gt;
&lt;p&gt;Comprehensions exist to make code more readable. If one stops being readable, use a loop. Watch for these warning signs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;It doesn&#39;t fit comfortably on one or two lines.&lt;/strong&gt; Nested comprehensions with several conditions are hard to scan.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You need side effects.&lt;/strong&gt; Don&#39;t write &lt;code&gt;[print(x) for x in items]&lt;/code&gt;. It builds a throwaway list of &lt;code&gt;None&lt;/code&gt; values. Use a &lt;code&gt;for&lt;/code&gt; loop.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You need &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;except&lt;/code&gt; or several statements per item.&lt;/strong&gt; A comprehension holds a single expression; a loop or a helper function is clearer.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;c1&#34;&gt;# Hard to read: avoid&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;result&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;y&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;y&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;y&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;y&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;

&lt;span class=&#34;c1&#34;&gt;# Clearer: give the logic a name&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;odd_pair_products&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;limit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;limit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;y&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;y&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
                &lt;span class=&#34;k&#34;&gt;yield&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;y&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;result&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;odd_pair_products&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;recap&#34;&gt;Recap&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;[expr for x in items]&lt;/code&gt; transforms every item.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[expr for x in items if cond]&lt;/code&gt; filters as well.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[a if cond else b for x in items]&lt;/code&gt; chooses a value for each item.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;{k: v ...}&lt;/code&gt; for dicts, &lt;code&gt;{x ...}&lt;/code&gt; for sets and &lt;code&gt;(x ...)&lt;/code&gt; for lazy generators.&lt;/li&gt;
&lt;li&gt;If it&#39;s getting clever, a loop is usually kinder to the next reader, who might be you.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Practice:&lt;/strong&gt; open a script you wrote recently and look for an &#34;empty list, loop, append&#34; pattern. Rewrite it as a comprehension, then decide honestly which version reads better.&lt;/p&gt;</content:encoded>
    </item>
    <item>
      <title>Understanding async/await in JavaScript</title>
      <link>https://cryo-networks.com/en/posts/javascript-async-await/</link>
      <guid isPermaLink="true">https://cryo-networks.com/en/posts/javascript-async-await/</guid>
      <pubDate>Wed, 21 Oct 2026 09:00:00 +0000</pubDate>
      <category>JavaScript</category>
      <description>A practical guide to promises and async/await: how they work, how to handle errors, and how to run tasks in parallel without slowing your app down.</description>
      <content:encoded>&lt;p&gt;JavaScript runs your code on a single thread, yet it can download files, wait for timers and talk to databases without freezing. The secret is &lt;strong&gt;asynchronous&lt;/strong&gt; code, and &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; is the most readable way to write it.&lt;/p&gt;
&lt;p&gt;In this article you&#39;ll learn what a promise actually is, how &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; builds on it, how to handle errors properly, and how to avoid the most common performance mistake developers make.&lt;/p&gt;
&lt;h2 id=&#34;the-problem-waiting-without-blocking&#34;&gt;The problem: waiting without blocking&lt;/h2&gt;
&lt;p&gt;Imagine fetching a user from an API. The network might take 300 ms. If JavaScript simply &lt;em&gt;stopped&lt;/em&gt; and waited, your whole page would freeze: no clicks, no scrolling, no animations.&lt;/p&gt;
&lt;p&gt;Instead, asynchronous functions return immediately with a &lt;strong&gt;promise&lt;/strong&gt;, an object that stands for a value that will be available &lt;em&gt;later&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&#34;promises-in-60-seconds&#34;&gt;Promises in 60 seconds&lt;/h2&gt;
&lt;p&gt;A promise is always in one of three states:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;pending&lt;/strong&gt;: the work is still in progress&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;fulfilled&lt;/strong&gt;: it finished successfully with a value&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;rejected&lt;/strong&gt;: it failed with an error&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You react to a promise with &lt;code&gt;.then()&lt;/code&gt; and &lt;code&gt;.catch()&lt;/code&gt;:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;nx&#34;&gt;fetch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;https://api.github.com/users/octocat&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Request failed:&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;This works, but chains get hard to follow as the logic grows, especially when one step depends on the result of another.&lt;/p&gt;
&lt;h2 id=&#34;enter-asyncawait&#34;&gt;Enter async/await&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; is syntax built on top of promises. It lets you write asynchronous code that &lt;em&gt;reads&lt;/em&gt; like synchronous code:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;async&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;showUser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;await&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fetch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`https://api.github.com/users/&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;await&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;nx&#34;&gt;showUser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;octocat&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Two rules make this work:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;async&lt;/code&gt; before a function&lt;/strong&gt; makes it always return a promise.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;await&lt;/code&gt; pauses that function&lt;/strong&gt; until the promise settles, then hands you its value. Other code keeps running in the meantime. Only &lt;em&gt;this&lt;/em&gt; function waits.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;admonition note&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;await doesn&#39;t block the page&lt;/p&gt;
&lt;p&gt;While &lt;code&gt;showUser&lt;/code&gt; is paused at an &lt;code&gt;await&lt;/code&gt;, the browser is free to handle clicks, run other functions and repaint the screen. The function resumes when the promise settles.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;handling-errors-with-trycatch&#34;&gt;Handling errors with try/catch&lt;/h2&gt;
&lt;p&gt;With &lt;code&gt;.then()&lt;/code&gt; chains you use &lt;code&gt;.catch()&lt;/code&gt;. With &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; you use the familiar &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;catch&lt;/code&gt;:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;async&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getUser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;await&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fetch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`https://api.github.com/users/&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;ow&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;ne&#34;&gt;Error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`HTTP &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;await&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`Could not load &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;:`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;message&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;admonition warning&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;fetch only rejects on network failure&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;404&lt;/code&gt; or &lt;code&gt;500&lt;/code&gt; response does &lt;strong&gt;not&lt;/strong&gt; reject the promise returned by &lt;code&gt;fetch()&lt;/code&gt;. Always check &lt;code&gt;response.ok&lt;/code&gt; (or &lt;code&gt;response.status&lt;/code&gt;) yourself, as shown above.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;the-1-mistake-accidentally-sequential-code&#34;&gt;The #1 mistake: accidentally sequential code&lt;/h2&gt;
&lt;p&gt;Look at this function. Can you spot the problem?&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;async&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;loadDashboard&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;await&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getUser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;octocat&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// ~300 ms&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;repos&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;await&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getRepos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;octocat&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// ~300 ms&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;events&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;await&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getEvents&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;octocat&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// ~300 ms&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;repos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;events&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// total: ~900 ms&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Each request waits for the previous one to finish, even though none of them depend on each other! Start them all at once and wait for them together with &lt;code&gt;Promise.all()&lt;/code&gt;:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;async&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;loadDashboard&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;repos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;events&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;await&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Promise&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;all&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getUser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;octocat&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getRepos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;octocat&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getEvents&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;octocat&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;repos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;events&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// total: ~300 ms&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Same result, roughly three times faster.&lt;/p&gt;
&lt;figure class=&#34;diagram&#34;&gt;&lt;figcaption&gt;Awaiting independent requests one by one adds their times together; Promise.all runs them at the same time&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&#34;choosing-the-right-promise-combinator&#34;&gt;Choosing the right Promise combinator&lt;/h2&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Fulfils when…&lt;/th&gt;
&lt;th&gt;Rejects when…&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Promise.all()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;all&lt;/strong&gt; promises fulfil&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;any&lt;/strong&gt; promise rejects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Promise.allSettled()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;all promises settle&lt;/td&gt;
&lt;td&gt;never&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Promise.race()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;the first promise to settle fulfils&lt;/td&gt;
&lt;td&gt;the first promise to settle rejects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Promise.any()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;the &lt;strong&gt;first&lt;/strong&gt; promise fulfils&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;all&lt;/strong&gt; promises reject&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Use &lt;code&gt;Promise.allSettled()&lt;/code&gt; when you want every result even if some fail, for example when loading several optional widgets:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;results&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;await&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Promise&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;allSettled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getRepos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;octocat&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getEvents&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;octocat&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)]);&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;of&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;results&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;===&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;fulfilled&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;render&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;warn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Widget failed:&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;reason&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;adding-a-timeout&#34;&gt;Adding a timeout&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;fetch()&lt;/code&gt; has no timeout by default. In modern browsers and Node.js 18+, &lt;code&gt;AbortSignal.timeout()&lt;/code&gt; gives you one in a single line:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;async&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fetchWithTimeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ms&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;5000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;await&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fetch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;signal&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;AbortSignal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ms&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;ow&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;ne&#34;&gt;Error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`HTTP &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;If the request takes longer than &lt;code&gt;ms&lt;/code&gt; milliseconds, the promise rejects with a &lt;code&gt;TimeoutError&lt;/code&gt;, which you can handle in a &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;catch&lt;/code&gt; like any other error.&lt;/p&gt;
&lt;h2 id=&#34;loops-and-await&#34;&gt;Loops and await&lt;/h2&gt;
&lt;p&gt;Using &lt;code&gt;await&lt;/code&gt; inside a &lt;code&gt;for...of&lt;/code&gt; loop processes items &lt;strong&gt;one at a time&lt;/strong&gt;. That&#39;s exactly what you want when order matters or when you must avoid hammering an API:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;of&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;orderIds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;await&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;processOrder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// waits for each one before starting the next&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;When the tasks are independent, map them to promises and wait for them together instead:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;await&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Promise&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;all&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;orderIds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;processOrder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)));&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;admonition danger&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;forEach doesn&#39;t wait&lt;/p&gt;
&lt;p&gt;&lt;code&gt;orderIds.forEach(async (id) =&amp;gt; await processOrder(id))&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; wait for anything, because &lt;code&gt;forEach&lt;/code&gt; ignores the promises your callback returns. Use &lt;code&gt;for...of&lt;/code&gt; or &lt;code&gt;Promise.all()&lt;/code&gt; instead.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;recap&#34;&gt;Recap&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;promise&lt;/strong&gt; represents a future value: pending, fulfilled or rejected.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;async&lt;/code&gt;&lt;/strong&gt; functions always return a promise; &lt;strong&gt;&lt;code&gt;await&lt;/code&gt;&lt;/strong&gt; pauses only that function.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;&lt;code&gt;try&lt;/code&gt;/&lt;code&gt;catch&lt;/code&gt;&lt;/strong&gt; for errors, and remember to check &lt;code&gt;response.ok&lt;/code&gt; with &lt;code&gt;fetch()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Run independent tasks in parallel with &lt;strong&gt;&lt;code&gt;Promise.all()&lt;/code&gt;&lt;/strong&gt; instead of awaiting them one by one.&lt;/li&gt;
&lt;li&gt;Never rely on &lt;strong&gt;&lt;code&gt;forEach&lt;/code&gt;&lt;/strong&gt; to wait for async callbacks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next time you write an &lt;code&gt;await&lt;/code&gt;, ask yourself: &lt;em&gt;does this line really need to wait for the one above it?&lt;/em&gt; That one question will make your apps noticeably faster.&lt;/p&gt;</content:encoded>
    </item>
    <item>
      <title>A Practical Git Branching Workflow for Beginners</title>
      <link>https://cryo-networks.com/en/posts/git-branching-workflow/</link>
      <guid isPermaLink="true">https://cryo-networks.com/en/posts/git-branching-workflow/</guid>
      <pubDate>Wed, 14 Oct 2026 09:00:00 +0000</pubDate>
      <category>Git</category>
      <description>Stop committing everything to main. Learn a simple, team-friendly branching workflow with feature branches, pull requests and clean merges.</description>
      <content:encoded>&lt;p&gt;When you first learn Git, it&#39;s natural to commit everything straight to &lt;code&gt;main&lt;/code&gt;. That works until you need to fix a bug halfway through a feature, or a teammate pushes changes that collide with yours.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Branches&lt;/strong&gt; solve this. In this guide you&#39;ll learn a simple workflow used by teams of every size: create a branch for each piece of work, commit freely, then merge it back when it&#39;s ready.&lt;/p&gt;
&lt;h2 id=&#34;what-a-branch-really-is&#34;&gt;What a branch really is&lt;/h2&gt;
&lt;p&gt;A branch in Git is just a &lt;strong&gt;movable pointer to a commit&lt;/strong&gt;. Creating one is instant and costs almost nothing, because Git doesn&#39;t copy your files. When you make a new commit, the branch you&#39;re on moves forward to point at it.&lt;/p&gt;
&lt;figure class=&#34;diagram&#34;&gt;&lt;figcaption&gt;A feature branch splits from main, gets its own commits, then merges back&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Here, &lt;code&gt;feature/login&lt;/code&gt; branches off &lt;code&gt;main&lt;/code&gt; after commit &lt;code&gt;C&lt;/code&gt; and gets two commits of its own, &lt;code&gt;D&lt;/code&gt; and &lt;code&gt;E&lt;/code&gt;. Work on one branch doesn&#39;t touch the other until you merge, which creates the merge commit &lt;code&gt;M&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;the-workflow-at-a-glance&#34;&gt;The workflow at a glance&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Update your local &lt;code&gt;main&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create a feature branch.&lt;/li&gt;
&lt;li&gt;Commit your work in small steps.&lt;/li&gt;
&lt;li&gt;Push the branch and open a pull request.&lt;/li&gt;
&lt;li&gt;Keep the branch up to date with &lt;code&gt;main&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Merge, then delete the branch.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&#39;s walk through each step.&lt;/p&gt;
&lt;h2 id=&#34;step-1-start-from-an-up-to-date-main&#34;&gt;Step 1: Start from an up-to-date main&lt;/h2&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;switch&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;main
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pull&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;origin&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;main&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;git switch&lt;/code&gt; (Git 2.23+) is the modern, clearer alternative to &lt;code&gt;git checkout&lt;/code&gt; for changing branches.&lt;/p&gt;
&lt;h2 id=&#34;step-2-create-a-feature-branch&#34;&gt;Step 2: Create a feature branch&lt;/h2&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;switch&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-c&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;feature/login-form&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;The &lt;code&gt;-c&lt;/code&gt; flag creates the branch and switches to it in one go. Use descriptive, prefixed names so everyone can tell what a branch is for:&lt;/p&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prefix&lt;/th&gt;
&lt;th&gt;Use it for&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;&lt;code&gt;feature/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;New functionality&lt;/td&gt;
&lt;td&gt;&lt;code&gt;feature/login-form&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fix/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bug fixes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fix/password-reset-email&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docs/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Documentation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docs/api-examples&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;refactor/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Code improvements with no behaviour change&lt;/td&gt;
&lt;td&gt;&lt;code&gt;refactor/user-service&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&#34;step-3-commit-in-small-meaningful-steps&#34;&gt;Step 3: Commit in small, meaningful steps&lt;/h2&gt;
&lt;p&gt;Make a change, review it, then commit:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;status&lt;span class=&#34;w&#34;&gt;              &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# what changed?&lt;/span&gt;
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;diff&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# review the exact changes&lt;/span&gt;
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;add&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;src/login.js&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# stage specific files&lt;/span&gt;
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;commit&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-m&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Add login form markup and validation&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;admonition tip&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;Write commit messages for your future self&lt;/p&gt;
&lt;p&gt;Use the imperative mood (&lt;em&gt;&#34;Add validation&#34;&lt;/em&gt;, not &lt;em&gt;&#34;Added validation&#34;&lt;/em&gt;) and describe &lt;strong&gt;what&lt;/strong&gt; changed and &lt;strong&gt;why&lt;/strong&gt;, not how. A good test: your message should complete the sentence &lt;em&gt;&#34;If applied, this commit will…&#34;&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;step-4-push-and-open-a-pull-request&#34;&gt;Step 4: Push and open a pull request&lt;/h2&gt;
&lt;p&gt;The first time you push a new branch, tell Git which remote branch it should track:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;push&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-u&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;origin&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;feature/login-form&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;-u&lt;/code&gt; sets the upstream, so afterwards a plain &lt;code&gt;git push&lt;/code&gt; is enough. On GitHub, GitLab or Bitbucket you&#39;ll now see a prompt to open a &lt;strong&gt;pull request&lt;/strong&gt; (called a &lt;em&gt;merge request&lt;/em&gt; on GitLab). This is where teammates review your code, leave comments and run automated tests before anything reaches &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;step-5-keep-your-branch-up-to-date&#34;&gt;Step 5: Keep your branch up to date&lt;/h2&gt;
&lt;p&gt;If &lt;code&gt;main&lt;/code&gt; moves on while you work, bring those changes into your branch so you resolve conflicts early and in small pieces:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;switch&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;main
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pull&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;origin&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;main
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;switch&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;feature/login-form
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;merge&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;main&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Some teams prefer &lt;code&gt;git rebase main&lt;/code&gt; instead of &lt;code&gt;git merge main&lt;/code&gt;. It replays your commits on top of the latest &lt;code&gt;main&lt;/code&gt; for a straight-line history. Both are valid, so follow your team&#39;s convention.&lt;/p&gt;
&lt;div class=&#34;admonition warning&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;Never rebase shared history&lt;/p&gt;
&lt;p&gt;Rebasing rewrites commits. Only rebase branches that nobody else has based work on. Otherwise you&#39;ll create painful conflicts for your teammates.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&#34;resolving-a-merge-conflict&#34;&gt;Resolving a merge conflict&lt;/h3&gt;
&lt;p&gt;A conflict happens when two branches change the same lines. Git pauses and marks the file:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; HEAD
const MAX_ATTEMPTS = 5;
=======
const MAX_ATTEMPTS = 3;
&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; main&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;The &lt;code&gt;HEAD&lt;/code&gt; section is your branch&#39;s version and the bottom section comes from &lt;code&gt;main&lt;/code&gt;. Edit the file to keep the correct version, delete the markers, then finish the merge:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;add&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;src/config.js
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;commit&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;step-6-merge-and-clean-up&#34;&gt;Step 6: Merge and clean up&lt;/h2&gt;
&lt;p&gt;Once the pull request is approved, merge it through your hosting platform. Then tidy up locally:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;switch&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;main
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pull&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;origin&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;main
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;branch&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-d&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;feature/login-form&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;git branch -d&lt;/code&gt; refuses to delete a branch that hasn&#39;t been merged, so it&#39;s a safe default. If your team uses &lt;em&gt;squash merges&lt;/em&gt;, Git may not recognise the branch as merged. Once you&#39;ve confirmed the pull request was merged, &lt;code&gt;git branch -D&lt;/code&gt; forces the deletion.&lt;/p&gt;
&lt;h2 id=&#34;handy-commands-to-remember&#34;&gt;Handy commands to remember&lt;/h2&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;branch&lt;span class=&#34;w&#34;&gt;                        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# list local branches&lt;/span&gt;
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;branch&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-a&lt;span class=&#34;w&#34;&gt;                     &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# include remote branches&lt;/span&gt;
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;log&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--oneline&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--graph&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--all&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# visualise history&lt;/span&gt;
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;switch&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-&lt;span class=&#34;w&#34;&gt;                      &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# jump back to the previous branch&lt;/span&gt;
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;stash&lt;span class=&#34;w&#34;&gt;                         &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# shelve uncommitted changes temporarily&lt;/span&gt;
git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;stash&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pop&lt;span class=&#34;w&#34;&gt;                     &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# bring them back&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;recap&#34;&gt;Recap&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A branch is a lightweight pointer, so create them freely.&lt;/li&gt;
&lt;li&gt;One branch per feature or fix keeps &lt;code&gt;main&lt;/code&gt; always deployable.&lt;/li&gt;
&lt;li&gt;Commit in small steps, write clear messages and open a pull request for review.&lt;/li&gt;
&lt;li&gt;Merge &lt;code&gt;main&lt;/code&gt; into your branch regularly to avoid giant conflicts.&lt;/li&gt;
&lt;li&gt;Delete branches after merging to keep your repository tidy.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Adopt this workflow even on solo projects. It builds habits that make joining a team effortless.&lt;/p&gt;</content:encoded>
    </item>
    <item>
      <title>Big-O Notation Explained with Real Code</title>
      <link>https://cryo-networks.com/en/posts/big-o-notation-explained/</link>
      <guid isPermaLink="true">https://cryo-networks.com/en/posts/big-o-notation-explained/</guid>
      <pubDate>Wed, 07 Oct 2026 09:00:00 +0000</pubDate>
      <category>Algorithms</category>
      <description>Understand time complexity without the maths anxiety. We&#39;ll analyse real Python functions and see why O(n²) code falls apart at scale.</description>
      <content:encoded>&lt;p&gt;Your code works on 100 items. Will it still work on 1,000,000? &lt;strong&gt;Big-O notation&lt;/strong&gt; answers that question. It describes how the running time (or memory use) of an algorithm &lt;em&gt;grows&lt;/em&gt; as its input gets bigger.&lt;/p&gt;
&lt;p&gt;You don&#39;t need advanced maths to use it. In this article we&#39;ll look at real functions, count the work they do, and build an intuition you can apply to your own code.&lt;/p&gt;
&lt;h2 id=&#34;the-core-idea-growth-not-speed&#34;&gt;The core idea: growth, not speed&lt;/h2&gt;
&lt;p&gt;Big-O doesn&#39;t tell you that a function takes 3 milliseconds. It tells you how the time &lt;strong&gt;scales&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Double the input of an &lt;strong&gt;O(n)&lt;/strong&gt; function and the time roughly doubles.&lt;/li&gt;
&lt;li&gt;Double the input of an &lt;strong&gt;O(n²)&lt;/strong&gt; function and the time roughly &lt;em&gt;quadruples&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We also drop constants and smaller terms. A function that does &lt;code&gt;3n + 10&lt;/code&gt; steps is simply &lt;strong&gt;O(n)&lt;/strong&gt;, because as &lt;code&gt;n&lt;/code&gt; gets large the &lt;code&gt;3&lt;/code&gt; and the &lt;code&gt;10&lt;/code&gt; stop mattering compared to the growth itself.&lt;/p&gt;
&lt;h2 id=&#34;o1-constant-time&#34;&gt;O(1): constant time&lt;/h2&gt;
&lt;p&gt;The work doesn&#39;t depend on the input size at all:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;get_first&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;is_even&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Indexing a list, looking up a key in a dictionary and appending to a list (on average) are all O(1).&lt;/p&gt;
&lt;h2 id=&#34;on-linear-time&#34;&gt;O(n): linear time&lt;/h2&gt;
&lt;p&gt;The work grows in direct proportion to the input. Looping over every item once is the classic example:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;find_max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;numbers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;largest&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;numbers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;numbers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;          &lt;span class=&#34;c1&#34;&gt;# runs n times&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;largest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;largest&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;largest&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Hidden O(n) operations are worth knowing too: &lt;code&gt;x in some_list&lt;/code&gt;, &lt;code&gt;some_list.index(x)&lt;/code&gt;, &lt;code&gt;sum()&lt;/code&gt; and &lt;code&gt;max()&lt;/code&gt; all scan the whole list.&lt;/p&gt;
&lt;h2 id=&#34;on2-quadratic-time&#34;&gt;O(n²): quadratic time&lt;/h2&gt;
&lt;p&gt;A loop inside a loop, where both run over the input, gives you roughly n × n steps:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;has_duplicates&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)):&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;j&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)):&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;# nested loop&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;j&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]:&lt;/span&gt;
                &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;For 1,000 items that&#39;s about half a million comparisons. For 1,000,000 items it&#39;s about &lt;strong&gt;500 billion&lt;/strong&gt;. That&#39;s the difference between instant and &#34;come back tomorrow&#34;.&lt;/p&gt;
&lt;h2 id=&#34;fixing-it-trade-memory-for-speed&#34;&gt;Fixing it: trade memory for speed&lt;/h2&gt;
&lt;p&gt;We can make &lt;code&gt;has_duplicates&lt;/code&gt; O(n) by remembering what we&#39;ve already seen in a &lt;strong&gt;set&lt;/strong&gt;, whose membership checks are O(1) on average:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;has_duplicates&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;seen&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;item&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;          &lt;span class=&#34;c1&#34;&gt;# runs n times&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;item&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;seen&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;# O(1) on average&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;seen&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;          &lt;span class=&#34;c1&#34;&gt;# O(1) on average&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Or, more concisely:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;has_duplicates&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;We spend a little extra memory (O(n) space) to save an enormous amount of time. This trade-off is one of the most common optimisations in real-world code.&lt;/p&gt;
&lt;h2 id=&#34;olog-n-logarithmic-time&#34;&gt;O(log n): logarithmic time&lt;/h2&gt;
&lt;p&gt;Logarithmic algorithms cut the problem in half at every step. &lt;strong&gt;Binary search&lt;/strong&gt; on a sorted list is the textbook example:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;binary_search&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sorted_items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;low&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;high&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sorted_items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;low&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;high&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;mid&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;low&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;high&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;//&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sorted_items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mid&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sorted_items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;low&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mid&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;       &lt;span class=&#34;c1&#34;&gt;# discard the left half&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;high&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mid&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;      &lt;span class=&#34;c1&#34;&gt;# discard the right half&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Searching 1,000,000 sorted items takes at most about &lt;strong&gt;20 steps&lt;/strong&gt;, because 2²⁰ ≈ 1,000,000. Double the data and you add just one more step.&lt;/p&gt;
&lt;h2 id=&#34;on-log-n-the-sweet-spot-for-sorting&#34;&gt;O(n log n): the sweet spot for sorting&lt;/h2&gt;
&lt;p&gt;Efficient general-purpose sorting algorithms run in &lt;strong&gt;O(n log n)&lt;/strong&gt;. That includes merge sort and Timsort, which powers Python&#39;s &lt;code&gt;sorted()&lt;/code&gt; and &lt;code&gt;list.sort()&lt;/code&gt;. It&#39;s only slightly worse than linear, and it&#39;s the best possible for sorting by comparing elements.&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;n&#34;&gt;names&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;sorted&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;names&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;# O(n log n)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;comparing-growth-rates&#34;&gt;Comparing growth rates&lt;/h2&gt;
&lt;p&gt;Approximate number of steps for different input sizes:&lt;/p&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Complexity&lt;/th&gt;
&lt;th&gt;n = 10&lt;/th&gt;
&lt;th&gt;n = 1,000&lt;/th&gt;
&lt;th&gt;n = 1,000,000&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;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O(log n)&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;1,000&lt;/td&gt;
&lt;td&gt;1,000,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O(n log n)&lt;/td&gt;
&lt;td&gt;33&lt;/td&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;20,000,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O(n²)&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;1,000,000&lt;/td&gt;
&lt;td&gt;1,000,000,000,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;figure class=&#34;diagram&#34;&gt;&lt;figcaption&gt;The same growth rates as curves: the gap between them widens dramatically as n grows&lt;/figcaption&gt;&lt;/figure&gt;
&lt;div class=&#34;admonition tip&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;A quick rule of thumb&lt;/p&gt;
&lt;p&gt;A modern CPU running compiled code manages very roughly 10⁸ simple operations per second, and Python manages fewer. If your algorithm needs 10¹² steps, no hardware upgrade will save you. A better algorithm will.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;how-to-analyse-your-own-code&#34;&gt;How to analyse your own code&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Find the loops.&lt;/strong&gt; A loop over the input is usually O(n). Nested loops over the input multiply: O(n²).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Look inside function calls.&lt;/strong&gt; &lt;code&gt;item in my_list&lt;/code&gt; inside a loop is a hidden nested loop.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep the biggest term.&lt;/strong&gt; O(n² + n) simplifies to O(n²).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Think about the worst case.&lt;/strong&gt; Unless stated otherwise, Big-O usually describes the worst case.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here&#39;s a sneaky example. What&#39;s its complexity?&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;common_items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;If &lt;code&gt;b&lt;/code&gt; is a list, &lt;code&gt;x in b&lt;/code&gt; scans it every time, so this is &lt;strong&gt;O(n × m)&lt;/strong&gt;. Convert &lt;code&gt;b&lt;/code&gt; to a set first and it drops to &lt;strong&gt;O(n + m)&lt;/strong&gt;:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;common_items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;b_set&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;                          &lt;span class=&#34;c1&#34;&gt;# O(m)&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b_set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;     &lt;span class=&#34;c1&#34;&gt;# O(n)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;recap&#34;&gt;Recap&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Big-O describes how work &lt;strong&gt;grows&lt;/strong&gt; with input size, ignoring constants.&lt;/li&gt;
&lt;li&gt;Common classes, fastest to slowest: O(1), O(log n), O(n), O(n log n), O(n²).&lt;/li&gt;
&lt;li&gt;Nested loops and hidden scans (&lt;code&gt;in&lt;/code&gt; on a list) are the usual culprits behind slow code.&lt;/li&gt;
&lt;li&gt;Sets and dictionaries turn O(n) lookups into O(1), which is often the easiest big win.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next time your code slows down as your data grows, don&#39;t reach for a faster server first. Reach for Big-O.&lt;/p&gt;</content:encoded>
    </item>
    <item>
      <title>CSS Flexbox vs Grid: When to Use Which</title>
      <link>https://cryo-networks.com/en/posts/css-flexbox-vs-grid/</link>
      <guid isPermaLink="true">https://cryo-networks.com/en/posts/css-flexbox-vs-grid/</guid>
      <pubDate>Wed, 30 Sep 2026 09:00:00 +0000</pubDate>
      <category>CSS</category>
      <description>Flexbox and Grid both lay out elements, but they think differently. Learn the one-dimension vs two-dimension rule and build real layouts with each.</description>
      <content:encoded>&lt;p&gt;Flexbox and CSS Grid are the two modern layout systems in CSS, and beginners often ask which one is &#34;better&#34;. The honest answer is neither. They solve different problems, and professional layouts use both, often on the same page.&lt;/p&gt;
&lt;h2 id=&#34;the-one-rule-to-remember&#34;&gt;The one rule to remember&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Flexbox&lt;/strong&gt; lays things out in &lt;strong&gt;one dimension&lt;/strong&gt;: a row &lt;em&gt;or&lt;/em&gt; a column.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Grid&lt;/strong&gt; lays things out in &lt;strong&gt;two dimensions&lt;/strong&gt;: rows &lt;em&gt;and&lt;/em&gt; columns at the same time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Another way to put it: Flexbox is &lt;strong&gt;content-first&lt;/strong&gt;. Items decide how much space they need, and the container distributes what&#39;s left. Grid is &lt;strong&gt;layout-first&lt;/strong&gt;. You define the tracks, then place items into them.&lt;/p&gt;
&lt;figure class=&#34;diagram&#34;&gt;&lt;figcaption&gt;Flexbox distributes items along a single axis; Grid aligns them on rows and columns at once&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&#34;flexbox-basics&#34;&gt;Flexbox basics&lt;/h2&gt;
&lt;p&gt;Turn any element into a flex container with &lt;code&gt;display: flex&lt;/code&gt;. Its direct children become flex items arranged in a row:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;toolbar&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;flex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;align-items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;center&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;/* alignment on the cross axis */&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;justify-content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;space-between&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;/* distribution on the main axis */&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;gap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;px&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;The properties you&#39;ll use 90% of the time:&lt;/p&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;flex-direction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;row&lt;/code&gt; (default) or &lt;code&gt;column&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;justify-content&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Distributes space along the main axis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;align-items&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Aligns items on the cross axis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gap&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Adds space between items&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;flex-wrap&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lets items wrap onto new lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;flex: 1&lt;/code&gt; (on an item)&lt;/td&gt;
&lt;td&gt;Makes the item grow to fill available space&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3 id=&#34;example-a-navigation-bar&#34;&gt;Example: a navigation bar&lt;/h3&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;nav&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;navbar&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;logo&amp;quot;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Brand&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ul&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;links&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;li&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;/docs&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Docs&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;li&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;li&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;/blog&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Blog&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;li&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ul&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;button&amp;quot;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;/signup&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Sign up&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;nav&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;navbar&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;flex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;align-items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;center&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;gap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;24&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;px&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;links&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;flex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;gap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;px&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;margin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;auto&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;/* the auto right margin pushes the button to the far right */&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;padding&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;list-style&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;none&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;admonition tip&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;The auto-margin trick&lt;/p&gt;
&lt;p&gt;In a flex container, &lt;code&gt;margin-left: auto&lt;/code&gt; or &lt;code&gt;margin-right: auto&lt;/code&gt; on an item absorbs all the free space on that side. It&#39;s the cleanest way to push one item away from the others.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&#34;example-perfect-centring&#34;&gt;Example: perfect centring&lt;/h3&gt;
&lt;p&gt;The famous &#34;how do I centre a div?&#34; question has a short answer with Flexbox:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;center&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;flex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;justify-content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;center&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;align-items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;center&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;With Grid, &lt;code&gt;display: grid; place-items: center;&lt;/code&gt; does the same thing.&lt;/p&gt;
&lt;h2 id=&#34;grid-basics&#34;&gt;Grid basics&lt;/h2&gt;
&lt;p&gt;Grid shines when you need items aligned in both directions. Define columns (and optionally rows) on the container:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;gallery&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;grid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;repeat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;gap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;px&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;The &lt;code&gt;fr&lt;/code&gt; unit means &#34;one fraction of the free space&#34;, so &lt;code&gt;repeat(3, 1fr)&lt;/code&gt; creates three equal columns.&lt;/p&gt;
&lt;h3 id=&#34;example-a-responsive-card-grid-with-no-media-queries&#34;&gt;Example: a responsive card grid with no media queries&lt;/h3&gt;
&lt;p&gt;This one line is worth memorising:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;cards&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;grid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;repeat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;auto&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;-fill&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;minmax&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;250&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;px&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;gap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;24&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;px&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;It says &lt;em&gt;&#34;fit as many columns as you can, each at least 250px wide, and stretch them to fill the row.&#34;&lt;/em&gt; On a phone you get one column; on a wide monitor you might get four, all without a single media query.&lt;/p&gt;
&lt;h3 id=&#34;example-a-full-page-layout&#34;&gt;Example: a full page layout&lt;/h3&gt;
&lt;p&gt;Grid&#39;s &lt;code&gt;grid-template-areas&lt;/code&gt; lets you draw your layout as text:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;grid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;240&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;px&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;grid-template-rows&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;auto&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fr&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;auto&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;grid-template-areas&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;header  header&amp;quot;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;sidebar main&amp;quot;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;footer  footer&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;min-height&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;vh&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;header&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;grid-area&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;header&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;aside&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;grid-area&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sidebar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;grid-area&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;footer&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;grid-area&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;footer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;media&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;max-width&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;700px&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;grid-template-rows&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;auto&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fr&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;auto&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;auto&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;grid-template-areas&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;header&amp;quot;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;main&amp;quot;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;sidebar&amp;quot;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;footer&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Notice how the mobile version simply rearranges the areas. The HTML doesn&#39;t change at all.&lt;/p&gt;
&lt;h2 id=&#34;side-by-side-comparison&#34;&gt;Side-by-side comparison&lt;/h2&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Flexbox&lt;/th&gt;
&lt;th&gt;Grid&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Dimensions&lt;/td&gt;
&lt;td&gt;One (row &lt;strong&gt;or&lt;/strong&gt; column)&lt;/td&gt;
&lt;td&gt;Two (rows &lt;strong&gt;and&lt;/strong&gt; columns)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Approach&lt;/td&gt;
&lt;td&gt;Content-first&lt;/td&gt;
&lt;td&gt;Layout-first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;Navbars, toolbars, button groups, centring&lt;/td&gt;
&lt;td&gt;Page layouts, galleries, dashboards, forms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sizing&lt;/td&gt;
&lt;td&gt;Items size themselves, then flex&lt;/td&gt;
&lt;td&gt;Tracks define sizes, items fill them&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overlapping items&lt;/td&gt;
&lt;td&gt;Awkward&lt;/td&gt;
&lt;td&gt;Easy: place items in the same cell&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&#34;using-them-together&#34;&gt;Using them together&lt;/h2&gt;
&lt;p&gt;Real interfaces nest the two. A typical pattern is &lt;strong&gt;Grid for the overall structure, Flexbox inside each component&lt;/strong&gt;:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;c&#34;&gt;/* Grid arranges the cards */&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;cards&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;grid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;repeat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;auto&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;-fill&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;minmax&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;250&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;px&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;gap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;24&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;px&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;c&#34;&gt;/* Flexbox arranges the content inside each card */&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;card&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;flex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;flex-direction&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;column&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;card&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;footer&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;margin-top&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;auto&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;/* pins the footer to the bottom of every card */&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Grid stretches every card in a row to the same height, and the &lt;code&gt;margin-top: auto&lt;/code&gt; trick keeps the footers aligned even when the cards contain different amounts of text. It&#39;s a small detail that makes designs look polished.&lt;/p&gt;
&lt;h2 id=&#34;quick-decision-guide&#34;&gt;Quick decision guide&lt;/h2&gt;
&lt;p&gt;Ask yourself:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Am I arranging items in a single line?&lt;/strong&gt; Use Flexbox.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Do I need items to line up in rows &lt;em&gt;and&lt;/em&gt; columns?&lt;/strong&gt; Use Grid.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Should the content decide the size?&lt;/strong&gt; Use Flexbox.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Should the layout decide the size?&lt;/strong&gt; Use Grid.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When in doubt, start with Flexbox for small components and Grid for anything that looks like a table, a gallery or a page skeleton.&lt;/p&gt;
&lt;h2 id=&#34;recap&#34;&gt;Recap&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Flexbox is one-dimensional and content-first, which makes it perfect for components.&lt;/li&gt;
&lt;li&gt;Grid is two-dimensional and layout-first, which makes it perfect for page structure and galleries.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;repeat(auto-fill, minmax(…, 1fr))&lt;/code&gt; gives you responsive grids without media queries.&lt;/li&gt;
&lt;li&gt;Combine them: Grid for the structure, Flexbox inside the pieces.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Open your browser&#39;s DevTools on any modern website and inspect the layout. You&#39;ll find both systems working together everywhere.&lt;/p&gt;</content:encoded>
    </item>
    <item>
      <title>SQL JOINs Explained with Simple Examples</title>
      <link>https://cryo-networks.com/en/posts/sql-joins-explained/</link>
      <guid isPermaLink="true">https://cryo-networks.com/en/posts/sql-joins-explained/</guid>
      <pubDate>Wed, 23 Sep 2026 09:00:00 +0000</pubDate>
      <category>SQL</category>
      <description>INNER, LEFT, RIGHT and FULL joins demystified with two tiny tables. See exactly which rows each join returns, and why.</description>
      <content:encoded>&lt;p&gt;Relational databases split data into separate tables to avoid repetition: customers in one table, orders in another. &lt;strong&gt;JOINs&lt;/strong&gt; are how you put that data back together when you query it.&lt;/p&gt;
&lt;p&gt;The best way to understand joins is to watch them work on real rows. We&#39;ll use two tiny tables throughout this guide.&lt;/p&gt;
&lt;h2 id=&#34;our-example-tables&#34;&gt;Our example tables&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;customers&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;name&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;Ada&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Grace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Linus&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;orders&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;49.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;15.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;99.99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;25.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Notice two things: &lt;strong&gt;Linus has no orders&lt;/strong&gt;, and &lt;strong&gt;order 104 belongs to customer 4, who doesn&#39;t exist&lt;/strong&gt; (perhaps the account was deleted). These edge cases are exactly where the different joins behave differently.&lt;/p&gt;
&lt;p&gt;Want to follow along? Here&#39;s the setup SQL, which works in SQLite, PostgreSQL and MySQL:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;TABLE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customers&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;INTEGER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;PRIMARY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;KEY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;TEXT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;TABLE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;INTEGER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;PRIMARY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;KEY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customer_id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;INTEGER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;total&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;DECIMAL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;INSERT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INTO&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customers&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;VALUES&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Ada&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Grace&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Linus&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;INSERT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INTO&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;VALUES&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;101&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;49&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;00&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;102&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;50&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;103&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;99&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;99&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;104&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;25&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;00&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Databases don&#39;t guarantee row order without &lt;code&gt;ORDER BY&lt;/code&gt;, so the results below are sorted for readability.&lt;/p&gt;
&lt;h2 id=&#34;inner-join-only-matching-rows&#34;&gt;INNER JOIN: only matching rows&lt;/h2&gt;
&lt;p&gt;An &lt;code&gt;INNER JOIN&lt;/code&gt; returns rows where the join condition matches in &lt;strong&gt;both&lt;/strong&gt; tables:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;order_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;total&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customers&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;c&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;INNER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;JOIN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customer_id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;order_id&lt;/th&gt;
&lt;th&gt;total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ada&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;49.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ada&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;15.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grace&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;99.99&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Linus disappears because he has no orders, and so does order 104 because it has no customer. Writing &lt;code&gt;JOIN&lt;/code&gt; on its own means &lt;code&gt;INNER JOIN&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;left-join-everything-from-the-left-table&#34;&gt;LEFT JOIN: everything from the left table&lt;/h2&gt;
&lt;p&gt;A &lt;code&gt;LEFT JOIN&lt;/code&gt; keeps &lt;strong&gt;every row from the left table&lt;/strong&gt; (the one after &lt;code&gt;FROM&lt;/code&gt;) and fills in &lt;code&gt;NULL&lt;/code&gt; where there&#39;s no match on the right:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;order_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;total&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customers&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;c&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;LEFT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;JOIN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customer_id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;order_id&lt;/th&gt;
&lt;th&gt;total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ada&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;49.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ada&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;15.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grace&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;99.99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linus&lt;/td&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Linus is back, with &lt;code&gt;NULL&lt;/code&gt;s in the order columns. After &lt;code&gt;INNER JOIN&lt;/code&gt;, this is the join you&#39;ll use most often.&lt;/p&gt;
&lt;h3 id=&#34;finding-rows-with-no-match&#34;&gt;Finding rows with no match&lt;/h3&gt;
&lt;p&gt;A classic trick is to combine &lt;code&gt;LEFT JOIN&lt;/code&gt; with &lt;code&gt;WHERE ... IS NULL&lt;/code&gt; to find &#34;orphans&#34;. Here we find customers who have never ordered:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customers&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;c&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;LEFT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;JOIN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customer_id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;IS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Result: &lt;strong&gt;Linus&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;right-join-everything-from-the-right-table&#34;&gt;RIGHT JOIN: everything from the right table&lt;/h2&gt;
&lt;p&gt;A &lt;code&gt;RIGHT JOIN&lt;/code&gt; is the mirror image: it keeps every row from the &lt;strong&gt;right&lt;/strong&gt; table.&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;order_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;total&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customers&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;c&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;RIGHT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;JOIN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customer_id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;order_id&lt;/th&gt;
&lt;th&gt;total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ada&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;49.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ada&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;15.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grace&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;99.99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;25.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Now the orphaned order 104 appears with no customer name. In practice, most developers rewrite right joins as left joins by swapping the table order. A query is easier to read when the &#34;keep everything&#34; table always comes first.&lt;/p&gt;
&lt;h2 id=&#34;full-outer-join-everything-from-both&#34;&gt;FULL OUTER JOIN: everything from both&lt;/h2&gt;
&lt;p&gt;A &lt;code&gt;FULL OUTER JOIN&lt;/code&gt; keeps all rows from both tables, matching them where it can:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;order_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;total&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customers&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;c&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;FULL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;OUTER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;JOIN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customer_id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;order_id&lt;/th&gt;
&lt;th&gt;total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ada&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;49.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ada&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;15.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grace&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;99.99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linus&lt;/td&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;25.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;It&#39;s handy for reconciling data: comparing two lists and spotting what&#39;s missing on either side.&lt;/p&gt;
&lt;div class=&#34;admonition note&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;Database support&lt;/p&gt;
&lt;p&gt;MySQL doesn&#39;t support &lt;code&gt;FULL OUTER JOIN&lt;/code&gt;; you can emulate it by combining a &lt;code&gt;LEFT JOIN&lt;/code&gt; and a &lt;code&gt;RIGHT JOIN&lt;/code&gt; with &lt;code&gt;UNION&lt;/code&gt;. SQLite supports &lt;code&gt;RIGHT&lt;/code&gt; and &lt;code&gt;FULL&lt;/code&gt; joins from version 3.39.0 onwards.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;summary-table&#34;&gt;Summary table&lt;/h2&gt;
&lt;figure class=&#34;diagram&#34;&gt;&lt;figcaption&gt;Which rows each join keeps, shown as Venn diagrams&lt;/figcaption&gt;&lt;/figure&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Join&lt;/th&gt;
&lt;th&gt;Keeps unmatched rows from…&lt;/th&gt;
&lt;th&gt;Typical use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;INNER JOIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;neither table&lt;/td&gt;
&lt;td&gt;Data that must exist on both sides&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LEFT JOIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;the left table&lt;/td&gt;
&lt;td&gt;&#34;All customers, plus their orders if any&#34;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RIGHT JOIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;the right table&lt;/td&gt;
&lt;td&gt;Rarely used; usually rewritten as a LEFT JOIN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FULL OUTER JOIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;both tables&lt;/td&gt;
&lt;td&gt;Reconciling two datasets&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&#34;aggregating-after-a-join&#34;&gt;Aggregating after a join&lt;/h2&gt;
&lt;p&gt;Joins become really powerful with &lt;code&gt;GROUP BY&lt;/code&gt;. Here&#39;s each customer&#39;s order count and total spend, including customers with zero orders:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;COUNT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;               &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;order_count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;COALESCE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SUM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;total&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;total_spent&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customers&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;c&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;LEFT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;JOIN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orders&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;customer_id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;GROUP&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;total_spent&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DESC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;order_count&lt;/th&gt;
&lt;th&gt;total_spent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Grace&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;99.99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ada&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;64.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linus&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;div class=&#34;admonition tip&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;COUNT(column) vs COUNT(*)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;COUNT(o.id)&lt;/code&gt; counts only non-NULL values, so Linus correctly gets &lt;code&gt;0&lt;/code&gt;. &lt;code&gt;COUNT(*)&lt;/code&gt; counts rows, and would give Linus &lt;code&gt;1&lt;/code&gt; because the LEFT JOIN still produces one row for him.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;common-mistakes&#34;&gt;Common mistakes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Filtering the right table in &lt;code&gt;WHERE&lt;/code&gt; after a &lt;code&gt;LEFT JOIN&lt;/code&gt;.&lt;/strong&gt; A condition like &lt;code&gt;WHERE o.total &amp;gt; 20&lt;/code&gt; silently removes the &lt;code&gt;NULL&lt;/code&gt; rows and turns your LEFT JOIN into an INNER JOIN. Move the condition into the &lt;code&gt;ON&lt;/code&gt; clause instead: &lt;code&gt;LEFT JOIN orders AS o ON o.customer_id = c.id AND o.total &amp;gt; 20&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Using the wrong join condition.&lt;/strong&gt; Joining on the wrong columns, or with no real condition at all, can produce a &lt;em&gt;Cartesian product&lt;/em&gt;: every row paired with every other row.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Joining on columns with different types.&lt;/strong&gt; This can stop the database from using its indexes, which makes queries slow.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;recap&#34;&gt;Recap&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;INNER JOIN&lt;/strong&gt; returns only rows that match on both sides.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LEFT JOIN&lt;/strong&gt; returns all rows from the left table, with &lt;code&gt;NULL&lt;/code&gt;s where there&#39;s no match.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RIGHT JOIN&lt;/strong&gt; is the mirror of LEFT and is usually rewritten as a LEFT JOIN.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FULL OUTER JOIN&lt;/strong&gt; returns everything from both tables.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LEFT JOIN ... WHERE right.id IS NULL&lt;/code&gt; finds rows without a match.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Set up the two tables above in SQLite (&lt;code&gt;sqlite3 shop.db&lt;/code&gt;) and run each query yourself. Five minutes of experimenting beats an hour of reading.&lt;/p&gt;</content:encoded>
    </item>
    <item>
      <title>Essential Linux Commands Every Developer Should Know</title>
      <link>https://cryo-networks.com/en/posts/linux-command-line-essentials/</link>
      <guid isPermaLink="true">https://cryo-networks.com/en/posts/linux-command-line-essentials/</guid>
      <pubDate>Wed, 16 Sep 2026 09:00:00 +0000</pubDate>
      <category>Linux</category>
      <description>Navigate, search, inspect and manage a Linux system with confidence. A practical tour of the commands developers reach for every day.</description>
      <content:encoded>&lt;p&gt;Whether you&#39;re deploying to a cloud server, working inside Docker, or just using a terminal on your Mac, sooner or later you&#39;ll be typing Linux commands. The good news is that a few dozen commands cover the vast majority of daily work.&lt;/p&gt;
&lt;p&gt;Each section below shows the commands, the options you&#39;ll actually use, and real-world examples.&lt;/p&gt;
&lt;h2 id=&#34;navigating-the-file-system&#34;&gt;Navigating the file system&lt;/h2&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;nb&#34;&gt;pwd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;                 &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# where am I?&lt;/span&gt;
ls&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-lah&lt;span class=&#34;w&#34;&gt;             &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# list everything: long format, hidden files, human-readable sizes&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;/var/log&lt;span class=&#34;w&#34;&gt;         &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# go to an absolute path&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;..&lt;span class=&#34;w&#34;&gt;               &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# up one level&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# jump back to the previous directory&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;                  &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# go home (same as cd ~)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;admonition tip&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;Tab completion saves hours&lt;/p&gt;
&lt;p&gt;Type the first few letters of a file or directory name and press &lt;kbd&gt;Tab&lt;/kbd&gt;. The shell completes it for you, or shows the options if there&#39;s more than one match.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;creating-copying-moving-and-deleting&#34;&gt;Creating, copying, moving and deleting&lt;/h2&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;mkdir&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-p&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;projects/blog/src&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# create nested directories in one go&lt;/span&gt;
touch&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;notes.txt&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# create an empty file (or update its timestamp)&lt;/span&gt;
cp&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;config.yml&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;config.yml.bak&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# copy a file&lt;/span&gt;
cp&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-r&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;src/&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;backup/&lt;span class=&#34;w&#34;&gt;             &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# copy a directory recursively&lt;/span&gt;
mv&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;draft.md&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;posts/final.md&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# move and/or rename&lt;/span&gt;
rm&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;old.log&lt;span class=&#34;w&#34;&gt;                     &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# delete a file&lt;/span&gt;
rm&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-r&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;build/&lt;span class=&#34;w&#34;&gt;                   &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# delete a directory and everything in it&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;admonition danger&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;There is no recycle bin&lt;/p&gt;
&lt;p&gt;&lt;code&gt;rm&lt;/code&gt; deletes immediately and permanently. Double-check paths before running &lt;code&gt;rm -r&lt;/code&gt;, especially with wildcards or &lt;code&gt;sudo&lt;/code&gt;. Adding &lt;code&gt;-i&lt;/code&gt; makes &lt;code&gt;rm&lt;/code&gt; ask before each deletion.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;reading-files&#34;&gt;Reading files&lt;/h2&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;cat&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;package.json&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# print a whole (short) file&lt;/span&gt;
less&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;/var/log/syslog&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# scroll through a long file (q to quit, / to search)&lt;/span&gt;
head&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-n&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;app.log&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# first 20 lines&lt;/span&gt;
tail&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-n&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;50&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;app.log&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# last 50 lines&lt;/span&gt;
tail&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-f&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;app.log&lt;span class=&#34;w&#34;&gt;         &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# follow new lines as they&amp;#39;re written, great for live logs&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;searching-inside-files-with-grep&#34;&gt;Searching inside files with grep&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;grep&lt;/code&gt; finds lines that match a pattern. It&#39;s one of the most useful tools you&#39;ll ever learn:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;grep&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;ERROR&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;app.log&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# lines containing ERROR&lt;/span&gt;
grep&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-i&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;timeout&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;app.log&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# case-insensitive&lt;/span&gt;
grep&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-rn&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;TODO&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;src/&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# search recursively, show line numbers&lt;/span&gt;
grep&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-v&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;DEBUG&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;app.log&lt;span class=&#34;w&#34;&gt;         &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# lines that do NOT match&lt;/span&gt;
grep&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-c&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;404&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;access.log&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# count matching lines&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;finding-files-with-find&#34;&gt;Finding files with find&lt;/h2&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;find&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;.&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-name&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;*.py&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;                       &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# all Python files below the current directory&lt;/span&gt;
find&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;/var/log&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-name&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;*.log&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-mtime&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-1&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# .log files modified in the last 24 hours&lt;/span&gt;
find&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;.&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-type&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;f&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-size&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;+100M&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# files bigger than 100 MB&lt;/span&gt;
find&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;.&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-type&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;d&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-name&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;node_modules&lt;span class=&#34;w&#34;&gt;         &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# directories named node_modules&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;pipes-and-redirection-combining-commands&#34;&gt;Pipes and redirection: combining commands&lt;/h2&gt;
&lt;p&gt;The real power of the command line comes from chaining small tools together. The pipe &lt;code&gt;|&lt;/code&gt; sends one command&#39;s output into the next command&#39;s input:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;c1&#34;&gt;# Top 5 IP addresses hitting your web server&lt;/span&gt;
cut&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-d&lt;span class=&#34;s1&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-f1&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;access.log&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sort&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;uniq&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-c&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sort&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-rn&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;head&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-5&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Read it left to right: take the first field (the IP address) from each line, sort the addresses, count the duplicates, sort by count with the highest first, and show the top five.&lt;/p&gt;
&lt;figure class=&#34;diagram&#34;&gt;&lt;figcaption&gt;Each command transforms the data and passes it to the next one through the pipe&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Redirection sends output to files:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;ls&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-la&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;files.txt&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# write output to a file (overwrites it)&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;done&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;build.log&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# append to a file&lt;/span&gt;
npm&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;test.log&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&amp;gt;&lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# capture both normal output and errors&lt;/span&gt;
wc&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-l&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;lt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;data.csv&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# use a file as input: counts its lines&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;permissions-with-chmod-and-chown&#34;&gt;Permissions with chmod and chown&lt;/h2&gt;
&lt;p&gt;Run &lt;code&gt;ls -l&lt;/code&gt; and you&#39;ll see lines like this:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;-rwxr-xr-- 1 deploy www-data 2048 Oct 12 09:30 deploy.sh&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;The permission string breaks down into three groups of three: the &lt;strong&gt;owner&lt;/strong&gt; (&lt;code&gt;rwx&lt;/code&gt;), the &lt;strong&gt;group&lt;/strong&gt; (&lt;code&gt;r-x&lt;/code&gt;) and &lt;strong&gt;everyone else&lt;/strong&gt; (&lt;code&gt;r--&lt;/code&gt;). Here &lt;code&gt;r&lt;/code&gt; means read, &lt;code&gt;w&lt;/code&gt; means write and &lt;code&gt;x&lt;/code&gt; means execute.&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;chmod&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;+x&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;deploy.sh&lt;span class=&#34;w&#34;&gt;                              &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# make a script executable&lt;/span&gt;
chmod&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;644&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;index.html&lt;span class=&#34;w&#34;&gt;                            &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# owner: read/write, everyone else: read-only&lt;/span&gt;
chmod&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;755&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;scripts/&lt;span class=&#34;w&#34;&gt;                              &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# owner: full access, everyone else: read and enter&lt;/span&gt;
sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;chown&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-R&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;www-data:www-data&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;/var/www/site&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# change owner and group recursively&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Numeric modes add up: read = 4, write = 2, execute = 1. So &lt;code&gt;7&lt;/code&gt; = 4+2+1 (&lt;code&gt;rwx&lt;/code&gt;), &lt;code&gt;5&lt;/code&gt; = 4+1 (&lt;code&gt;r-x&lt;/code&gt;) and &lt;code&gt;4&lt;/code&gt; means read-only.&lt;/p&gt;
&lt;h2 id=&#34;processes-whats-running&#34;&gt;Processes: what&#39;s running?&lt;/h2&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;ps&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;aux&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;grep&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;node&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# find running node processes&lt;/span&gt;
top&lt;span class=&#34;w&#34;&gt;                         &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# live view of CPU and memory use (q to quit)&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;kill&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;12345&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;                  &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# politely ask process 12345 to stop&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;kill&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-9&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;12345&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;               &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# force it to stop (last resort)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Many systems also have &lt;code&gt;htop&lt;/code&gt;, a friendlier, colourful alternative to &lt;code&gt;top&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;disk-and-memory&#34;&gt;Disk and memory&lt;/h2&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;df&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-h&lt;span class=&#34;w&#34;&gt;               &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# free disk space per filesystem&lt;/span&gt;
du&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-sh&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;*&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# size of each item in the current directory&lt;/span&gt;
du&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-sh&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;*&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sort&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-h&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# ...sorted from smallest to largest&lt;/span&gt;
free&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-h&lt;span class=&#34;w&#34;&gt;             &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# memory usage&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;networking-basics&#34;&gt;Networking basics&lt;/h2&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;curl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;https://example.com&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# fetch a page&lt;/span&gt;
curl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-I&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;https://example.com&lt;span class=&#34;w&#34;&gt;       &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# headers only: check status codes and caching&lt;/span&gt;
curl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-X&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;POST&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-H&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Content-Type: application/json&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;-d&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;{&amp;quot;name&amp;quot;: &amp;quot;Ada&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;https://httpbin.org/post
sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;ss&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-tlnp&lt;span class=&#34;w&#34;&gt;                     &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# which programs are listening on which ports&lt;/span&gt;
ping&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-c&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;example.com&lt;span class=&#34;w&#34;&gt;             &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# is the host reachable?&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;working-on-remote-servers&#34;&gt;Working on remote servers&lt;/h2&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;ssh&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;user@203.0.113.10&lt;span class=&#34;w&#34;&gt;                                 &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# log in to a server&lt;/span&gt;
scp&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;report.pdf&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;user@203.0.113.10:~/&lt;span class=&#34;w&#34;&gt;                   &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# copy a file to it&lt;/span&gt;
rsync&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-avz&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;./site/&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;user@203.0.113.10:/var/www/site/&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# sync a whole directory efficiently&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;getting-help&#34;&gt;Getting help&lt;/h2&gt;
&lt;p&gt;You don&#39;t need to memorise every option, because the system documents itself:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;man&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;grep&lt;span class=&#34;w&#34;&gt;                 &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# the full manual (q to quit)&lt;/span&gt;
grep&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--help&lt;span class=&#34;w&#34;&gt;              &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# a quick summary of options&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;history&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;grep&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;docker&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# find that command you ran last week&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;And here&#39;s the best shortcut of all: press &lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;R&lt;/kbd&gt; and start typing to search your command history interactively.&lt;/p&gt;
&lt;h2 id=&#34;recap&#34;&gt;Recap&lt;/h2&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Commands&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Navigate&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pwd&lt;/code&gt;, &lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;cd&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manage files&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mkdir&lt;/code&gt;, &lt;code&gt;touch&lt;/code&gt;, &lt;code&gt;cp&lt;/code&gt;, &lt;code&gt;mv&lt;/code&gt;, &lt;code&gt;rm&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read files&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cat&lt;/code&gt;, &lt;code&gt;less&lt;/code&gt;, &lt;code&gt;head&lt;/code&gt;, &lt;code&gt;tail -f&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search&lt;/td&gt;
&lt;td&gt;&lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;find&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Combine&lt;/td&gt;
&lt;td&gt;pipes, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;2&amp;gt;&amp;amp;1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Permissions&lt;/td&gt;
&lt;td&gt;&lt;code&gt;chmod&lt;/code&gt;, &lt;code&gt;chown&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ps&lt;/code&gt;, &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;kill&lt;/code&gt;, &lt;code&gt;df&lt;/code&gt;, &lt;code&gt;du&lt;/code&gt;, &lt;code&gt;free&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network&lt;/td&gt;
&lt;td&gt;&lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;ss&lt;/code&gt;, &lt;code&gt;ssh&lt;/code&gt;, &lt;code&gt;rsync&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Open a terminal and try each command as you read. Muscle memory is the real goal, and within a week you&#39;ll wonder how you ever worked without them.&lt;/p&gt;</content:encoded>
    </item>
    <item>
      <title>Python Decorators Explained Step by Step</title>
      <link>https://cryo-networks.com/en/posts/python-decorators-explained/</link>
      <guid isPermaLink="true">https://cryo-networks.com/en/posts/python-decorators-explained/</guid>
      <pubDate>Wed, 09 Sep 2026 09:00:00 +0000</pubDate>
      <category>Python</category>
      <description>Decorators look like magic, but they&#39;re just functions that wrap functions. Build your own timing and retry decorators from scratch.</description>
      <content:encoded>&lt;p&gt;You&#39;ve probably seen code like this in Flask, FastAPI or Django:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;nd&#34;&gt;@app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;home&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;Hello!&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;That &lt;code&gt;@&lt;/code&gt; line is a &lt;strong&gt;decorator&lt;/strong&gt;. Decorators let you add behaviour to a function (logging, timing, caching, access control) without changing the function&#39;s own code. They can look like magic, but they&#39;re built from three simple ideas. Let&#39;s take them one at a time.&lt;/p&gt;
&lt;h2 id=&#34;idea-1-functions-are-objects&#34;&gt;Idea 1: functions are objects&lt;/h2&gt;
&lt;p&gt;In Python, functions are values like any other. You can assign them to variables and pass them to other functions:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;greet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Hello, &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;!&amp;quot;&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;say_hello&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;greet&lt;/span&gt;          &lt;span class=&#34;c1&#34;&gt;# no parentheses: we&amp;#39;re not calling it&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;say_hello&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Ada&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;# Hello, Ada!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;idea-2-functions-can-be-defined-inside-functions&#34;&gt;Idea 2: functions can be defined inside functions&lt;/h2&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;outer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;inner&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;quot;I&amp;#39;m inside!&amp;quot;&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;inner&lt;/span&gt;           &lt;span class=&#34;c1&#34;&gt;# return the function itself&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;outer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;                &lt;span class=&#34;c1&#34;&gt;# I&amp;#39;m inside!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;idea-3-inner-functions-remember-their-surroundings&#34;&gt;Idea 3: inner functions remember their surroundings&lt;/h2&gt;
&lt;p&gt;An inner function can use variables from the function that created it, even after that outer function has finished. This is called a &lt;strong&gt;closure&lt;/strong&gt;:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;make_multiplier&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;factor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;multiply&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;factor&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# &amp;quot;remembers&amp;quot; factor&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;multiply&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;make_multiplier&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;triple&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;make_multiplier&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;double&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;triple&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# 10 15&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&#34;putting-it-together-your-first-decorator&#34;&gt;Putting it together: your first decorator&lt;/h2&gt;
&lt;p&gt;A decorator is a function that &lt;strong&gt;takes a function and returns a new function&lt;/strong&gt;, usually one that wraps the original with extra behaviour:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;shout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;wrapper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kwargs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;result&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kwargs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;upper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;wrapper&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;greet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Hello, &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;!&amp;quot;&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;greet&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;shout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;greet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;       &lt;span class=&#34;c1&#34;&gt;# wrap it manually&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;greet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Ada&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;# HELLO, ADA!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;The &lt;code&gt;@&lt;/code&gt; syntax is simply a shortcut for that &lt;code&gt;greet = shout(greet)&lt;/code&gt; line:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;nd&#34;&gt;@shout&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;greet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Hello, &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;!&amp;quot;&lt;/span&gt;

&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;greet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Ada&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;# HELLO, ADA!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure class=&#34;diagram&#34;&gt;&lt;figcaption&gt;What the @ line actually does: the decorator receives your function and returns a replacement&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;*args&lt;/code&gt; and &lt;code&gt;**kwargs&lt;/code&gt; let the wrapper accept any arguments and pass them straight through, so the decorator works on functions with any signature.&lt;/p&gt;
&lt;h2 id=&#34;dont-forget-functoolswraps&#34;&gt;Don&#39;t forget functools.wraps&lt;/h2&gt;
&lt;p&gt;Our decorator has a subtle problem: the wrapped function has lost its identity.&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;greet&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;vm&#34;&gt;__name__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;      &lt;span class=&#34;c1&#34;&gt;# wrapper&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;That breaks debugging, documentation tools and some frameworks. Fix it with &lt;code&gt;functools.wraps&lt;/code&gt;, which copies the original function&#39;s name, docstring and other metadata onto the wrapper:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;functools&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;wraps&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;shout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;nd&#34;&gt;@wraps&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;wrapper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kwargs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kwargs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;upper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;wrapper&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;div class=&#34;admonition tip&#34;&gt;
&lt;p class=&#34;admonition-title&#34;&gt;Make it a habit&lt;/p&gt;
&lt;p&gt;Add &lt;code&gt;@wraps(func)&lt;/code&gt; to every decorator you write. It costs one line and prevents confusing bugs later.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;a-practical-decorator-timing&#34;&gt;A practical decorator: timing&lt;/h2&gt;
&lt;p&gt;Let&#39;s build something useful: a decorator that reports how long a function takes.&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;time&lt;/span&gt;
&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;functools&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;wraps&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;timed&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;nd&#34;&gt;@wraps&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;wrapper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kwargs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;start&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;perf_counter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kwargs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;finally&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;elapsed&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;perf_counter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start&lt;/span&gt;
            &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;vm&#34;&gt;__name__&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; took &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;elapsed&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;.3f&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;s&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;wrapper&lt;/span&gt;

&lt;span class=&#34;nd&#34;&gt;@timed&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;build_report&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rows&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;sum&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rows&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;build_report&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;5_000_000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;# build_report took 0.312s&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;The &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;finally&lt;/code&gt; makes sure the time is printed even if the function raises an exception.&lt;/p&gt;
&lt;h2 id=&#34;decorators-that-take-arguments&#34;&gt;Decorators that take arguments&lt;/h2&gt;
&lt;p&gt;What if you want to write &lt;code&gt;@retry(times=3)&lt;/code&gt;? Then you need &lt;strong&gt;one more layer&lt;/strong&gt;: a function that takes the arguments and &lt;em&gt;returns a decorator&lt;/em&gt;.&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;time&lt;/span&gt;
&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;functools&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;wraps&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;retry&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;times&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;delay&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;1.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;exceptions&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;ne&#34;&gt;Exception&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,)):&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decorator&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;nd&#34;&gt;@wraps&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;wrapper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kwargs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;attempt&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;times&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
                &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
                    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kwargs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
                &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;exceptions&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
                    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;attempt&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;times&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
                        &lt;span class=&#34;k&#34;&gt;raise&lt;/span&gt;
                    &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Attempt &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;attempt&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; failed: &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;. Retrying in &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delay&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;s…&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
                    &lt;span class=&#34;n&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sleep&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;wrapper&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;decorator&lt;/span&gt;

&lt;span class=&#34;nd&#34;&gt;@retry&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;times&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;delay&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0.5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;exceptions&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;ne&#34;&gt;ConnectionError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,))&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;fetch_prices&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;
    &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;@retry(times=5)&lt;/code&gt; first &lt;em&gt;calls&lt;/em&gt; &lt;code&gt;retry(...)&lt;/code&gt;, which returns &lt;code&gt;decorator&lt;/code&gt;, which is then applied to &lt;code&gt;fetch_prices&lt;/code&gt;. Three layers sounds complicated, but each one has a single job:&lt;/p&gt;
&lt;div class=&#34;table-wrap&#34;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Receives&lt;/th&gt;
&lt;th&gt;Returns&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;retry&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The options (&lt;code&gt;times&lt;/code&gt;, &lt;code&gt;delay&lt;/code&gt;…)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;decorator&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;decorator&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The function to wrap&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wrapper&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wrapper&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The arguments of each call&lt;/td&gt;
&lt;td&gt;The function&#39;s result&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&#34;built-in-decorators-you-should-know&#34;&gt;Built-in decorators you should know&lt;/h2&gt;
&lt;p&gt;Python ships with several excellent decorators. For example, you don&#39;t need to write your own caching, because &lt;code&gt;functools.cache&lt;/code&gt; (Python 3.9+) does it for you:&lt;/p&gt;
&lt;figure class=&#34;code-block&#34;&gt;&lt;pre class=&#34;highlight&#34;&gt;&lt;code&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;functools&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cache&lt;/span&gt;

&lt;span class=&#34;nd&#34;&gt;@cache&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;fibonacci&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fibonacci&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fibonacci&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fibonacci&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;# 354224848179261915075, instantly&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Without &lt;code&gt;@cache&lt;/code&gt;, this naive recursion would make more than 10²⁰ function calls. With it, each value is computed exactly once. Use &lt;code&gt;functools.lru_cache(maxsize=…)&lt;/code&gt; if you need to cap memory use.&lt;/p&gt;
&lt;p&gt;Other built-in decorators worth knowing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@property&lt;/code&gt; exposes a method as a read-only attribute.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@staticmethod&lt;/code&gt; and &lt;code&gt;@classmethod&lt;/code&gt; define methods that don&#39;t need an instance.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@dataclass&lt;/code&gt; generates &lt;code&gt;__init__&lt;/code&gt;, &lt;code&gt;__repr__&lt;/code&gt; and &lt;code&gt;__eq__&lt;/code&gt; for you.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@contextlib.contextmanager&lt;/code&gt; turns a generator into a &lt;code&gt;with&lt;/code&gt;-statement context manager.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;recap&#34;&gt;Recap&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Functions are objects: they can be passed around, returned and defined inside other functions.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;decorator&lt;/strong&gt; takes a function and returns a wrapped version; &lt;code&gt;@deco&lt;/code&gt; is shorthand for &lt;code&gt;func = deco(func)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;*args, **kwargs&lt;/code&gt; to support any signature, and &lt;strong&gt;always&lt;/strong&gt; use &lt;code&gt;@functools.wraps&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Decorators with arguments need one extra layer: a function that returns the decorator.&lt;/li&gt;
&lt;li&gt;Reach for built-ins like &lt;code&gt;@cache&lt;/code&gt;, &lt;code&gt;@property&lt;/code&gt; and &lt;code&gt;@dataclass&lt;/code&gt; before writing your own.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Challenge:&lt;/strong&gt; write a &lt;code&gt;@log_calls&lt;/code&gt; decorator that prints the function&#39;s name and arguments every time it&#39;s called. Then stack it with &lt;code&gt;@timed&lt;/code&gt; and observe the order in which they run.&lt;/p&gt;</content:encoded>
    </item>
  </channel>
</rss>