<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Sergi&#x27;s writing</title>
    <subtitle>Blog of Sergi Pons Freixes</subtitle>
    <link rel="self" type="application/atom+xml" href="https://sergiswriting.com/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://sergiswriting.com"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-04-29T00:00:00+00:00</updated>
    <id>https://sergiswriting.com/atom.xml</id>
    <entry xml:lang="en">
        <title>This blog got a new favicon thanks to the Small Web</title>
        <published>2026-04-29T00:00:00+00:00</published>
        <updated>2026-04-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/new-favicon-small-web/"/>
        <id>https://sergiswriting.com/new-favicon-small-web/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/new-favicon-small-web/">&lt;p&gt;The other day I was checking out what was surfacing on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bubbles.town&#x2F;&quot;&gt;Bubbles&lt;&#x2F;a&gt;, and I ended up reading this
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mattstein.com&#x2F;thoughts&#x2F;hand-drawn-favicons&#x2F;?ref=bubbles.town&quot;&gt;post from Matt Stein about how he likes to hand-draw favicons&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;At the end of the post, he mentions he would love to make favicons for others, so I decided to reach out and ask him
to make one for this blog. After a few emails back and forth, I ended up with this beauty in PNG form in my possession:&lt;&#x2F;p&gt;
&lt;figure&gt;




    &lt;img class=&quot;[ post-img ] [ post-img--borderless ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;favicon-original.83eed6585ba55bf9.png&quot; alt=&quot;Favicon for my blog handcrafted by Matt
Stein. It&amp;#x27;s a green circle with a black S inside. The S looks like a scribble, and at the top there is the point of a
pen drawing it.&quot;&gt;


&lt;&#x2F;figure&gt;
&lt;p&gt;Then I processed it to get three new versions (ICO, SVG, and &quot;Apple-friendly&quot; PNG) as
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;evilmartians.com&#x2F;chronicles&#x2F;how-to-favicon-in-2021-six-files-that-fit-most-needs&quot;&gt;recommended by Andrey Sitnik&lt;&#x2F;a&gt;,
and voilà, new favicon online!&lt;&#x2F;p&gt;
&lt;p&gt;I find all this beautiful: one person who loves their craft, helping others without expecting anything in return, just
for the joy of having another opportunity to practice their skill and share it with others. This is the web we need
to get back to. A web of personal, non-transactional exchanges. A web that creates trust and community. A web that
algorithm-driven platforms often fail to foster.&lt;&#x2F;p&gt;
&lt;p&gt;Happy websurfing!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Ditch Postman, use Bruno</title>
        <published>2026-01-20T00:00:00+00:00</published>
        <updated>2026-01-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/ditch-postman-use-bruno/"/>
        <id>https://sergiswriting.com/ditch-postman-use-bruno/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/ditch-postman-use-bruno/">&lt;p&gt;I had been using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.postman.com&#x2F;&quot;&gt;Postman&lt;&#x2F;a&gt; since the beginning of my web developer career. Back when I started using it, it was a great tool,
and I never felt the need to switch to something else. I remember some terminal-oriented alternatives popping up at some
point, but I develop on a desktop environment,
so I didn&#x27;t really had the need to lose the affordances a full graphic user interface offers.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m a fan of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.jetbrains.com&#x2F;pycharm&#x2F;&quot;&gt;Pycharm&lt;&#x2F;a&gt;, so I experimented a bit with its different iterations of
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.jetbrains.com&#x2F;help&#x2F;pycharm&#x2F;http-client-in-product-code-editor.html&quot;&gt;HTTP clients&lt;&#x2F;a&gt;, but they never clicked for
me.&lt;&#x2F;p&gt;
&lt;p&gt;But — and I can&#x27;t pinpoint when — I started noticing Postman was becoming more and more sluggish. It would take a looong time
to startup, and it wasn&#x27;t very responsive. I kept thinking &quot;I should look for some alternative,&quot; but, you know, was never finding the time
for it. Until one day, the level of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Enshittification&quot;&gt;enshittification&lt;&#x2F;a&gt; crossed a red line.&lt;&#x2F;p&gt;
&lt;p&gt;We had a networking issue at work, and we couldn&#x27;t reach the Internet. That was OK for the kind of work I was doing that day, as
our servers were on-premise. I was churning through my tasks, and at one point I wanted to manually test some APIs. I fired
up Postman expecting to load my pre-saved configurations&#x2F;requests… and I was greeted with the error message that it
could not connect to the Postman servers so my data could not be retrieved. Hence, I could not use it, even if I had no need
to connect to the Internet.&lt;&#x2F;p&gt;
&lt;p&gt;To make it clear, I did not (purposely) use any of the &quot;cloud&quot; features of Postman. My current employer is very strict about
keeping any data and code internally, so I don&#x27;t even want a third party to have a list of our endpoints to avoid any issues.
So, even if I had taken precautions, here we were, with my data being stored remotely and with an unusable tool.&lt;&#x2F;p&gt;
&lt;p&gt;That same day — as soon as Internet connectivity was back — I looked for alternatives. I knew about &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;insomnia.rest&#x2F;&quot;&gt;Insomnia&lt;&#x2F;a&gt;, but after
installing it I was greeted with a &quot;create account&quot; screen that made me shiver. I kept searching and eventually found
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.usebruno.com&#x2F;&quot;&gt;Bruno&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Bruno is all the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.usebruno.com&#x2F;compare&#x2F;bruno-vs-postman&quot;&gt;opposite of Postman&lt;&#x2F;a&gt;. It works by storing
your collections of requests locally as simple
text files, so you can easily understand where the data is stored and how. Every time you create a new collection, it will
ask you where you want to create the directory to contain the data files, so you can even keep things in different places
if you need to. That text format also enables you to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.usebruno.com&#x2F;git-integration&#x2F;overview&quot;&gt;share your configurations with your team via regular source control (aka git)&lt;&#x2F;a&gt;.
It&#x27;s snappy, pretty, and intuitive to use if you are familiar with similar tools like Postman or Insomnia. I haven&#x27;t missed a single feature from Postman.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve been using Bruno for a few months now, and I&#x27;m not looking back.&lt;&#x2F;p&gt;
&lt;p&gt;I strongly recommend that you give it a try and also check out their &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.usebruno.com&#x2F;introduction&#x2F;what-is-bruno&quot;&gt;documentation&lt;&#x2F;a&gt; to see all their features.&lt;&#x2F;p&gt;
&lt;p&gt;Happy HTTPing!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>My favourite Python library for calculating dice probabilities</title>
        <published>2025-12-11T00:00:00+00:00</published>
        <updated>2025-12-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/best-python-library-for-dice-probabilities/"/>
        <id>https://sergiswriting.com/best-python-library-for-dice-probabilities/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/best-python-library-for-dice-probabilities/">&lt;p&gt;Disclaimer: I am not a mathematician, and it has been a long time since I studied statistics and probability, so I&#x27;m probably botching or misusing some terms.&lt;&#x2F;p&gt;
&lt;p&gt;Every time I get into some miniatures wargame like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.atomicmassgames.com&#x2F;mcp&#x2F;&quot;&gt;Marvel Crisis Protocol&lt;&#x2F;a&gt;
or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ttcombat.com&#x2F;collections&#x2F;dropfleet-commander&quot;&gt;Dropfleet Commander&lt;&#x2F;a&gt;, one of the first things
I do is checking if there exists some dice roll simulator for that game. I use that to get a better feel of how some rules translate to
probabilities of success in the game.&lt;&#x2F;p&gt;
&lt;p&gt;If the game is very niche or very new, that utility doesn&#x27;t exist yet, so sometimes I build my own thing in Python. If I get very excited
about the game, I even make a web app to share with the community. An example of that
is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;arcanestats.cub3.net&quot;&gt;Arcanestats&lt;&#x2F;a&gt;
for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.kickstarter.com&#x2F;projects&#x2F;privateerpress&#x2F;warcaster-neo-mechanika&quot;&gt;Warcaster Neo-Mechanika&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I used to build these simulators using Python&#x27;s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;fractions.html&quot;&gt;fractions&lt;&#x2F;a&gt; and
crunching the numbers myself, until I discovered &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pypi.org&#x2F;project&#x2F;dice-stats&#x2F;&quot;&gt;Dice Stats&lt;&#x2F;a&gt;. This library is
great!
You might think &quot;hey, this thing is unmaintained, it hasn&#x27;t had a commit since 2020!&quot; Well, no, it&#x27;s just &lt;em&gt;done&lt;&#x2F;em&gt;. It
does what it needs to do, and it&#x27;s bug free — &lt;abbr title=&quot;As Far As I Know&quot;&gt;AFAIK&lt;&#x2F;abbr&gt;.
So why would they be pushing more commits? It even has
a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;peilonrayz.github.io&#x2F;dice_stats&#x2F;public&#x2F;public.html&quot;&gt;Read the Docs&lt;&#x2F;a&gt; page!&lt;&#x2F;p&gt;
&lt;p&gt;The documentation is enough to get you started, but I am going to share some code from Arcanestats as a real
example.
Warcaster Neo-Mechanika uses custom 6-sided dice, where each side has a number of successes, called strikes. There are
two types of dice, and the number of strikes is different for each. An &lt;em&gt;Action die&lt;&#x2F;em&gt; has 3 sides with 0 strikes, 2
sides with 1 strike, and 1 side with 2 strikes. A &lt;em&gt;Power die&lt;&#x2F;em&gt; has 1 side with 0 strikes, 4 sides with 1 strike,
and 1 side with 2 strikes. As you can see, Power dice are better than Action dice, as they have more strikes on average.
With Dice Stats, these dice can be modeled as:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; fractions&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; Fraction&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; dice_stats&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; Dice&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt;ACTION_DIE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; Dice({&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;: Fraction(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;: Fraction(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span&gt;: Fraction(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span&gt;)})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt;POWER_DIE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; Dice({&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;: Fraction(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;: Fraction(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span&gt;: Fraction(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span&gt;)})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;span id=&quot;roll-definition&quot;&gt;On a single roll&lt;&#x2F;span&gt;, you can roll a few Action dice and a few Power dice together. To calculate the outcome of that, you
can write a function like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; typing&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; NamedTuple&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Roll&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; NamedTuple(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;Roll&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, [(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;action&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; int&lt;&#x2F;span&gt;&lt;span&gt;), (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;power&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; int&lt;&#x2F;span&gt;&lt;span&gt;)])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt; get_dice&lt;&#x2F;span&gt;&lt;span&gt;(roll: Roll) -&amp;gt; Dice:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt; ACTION_DIE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; roll.action&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt; POWER_DIE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; roll.power&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The outcome is an instance of &lt;code&gt;Dice&lt;&#x2F;code&gt;, because a &lt;code&gt;Dice&lt;&#x2F;code&gt; is like a mapping of each outcome — in this case, number of
strikes —
and their probability.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s see something more complex: the whole attack sequence. In the game, when one model attacks another model,
the attacker player rolls a set of attack dice, and the defender player rolls a set of defense dice. Each player rolls a
combination of Action
and Power dice. The amount of dice depends on the models&#x27; attack&#x2F;defense stats, game circumstances like being in cover,
etc. Then, we calculate the difference between the two rolls — strikes on attack minus strikes on defense. If the
result is 0 or negative, the attack completely misses. Otherwise, we need to calculate how much damage the defender
takes.&lt;&#x2F;p&gt;
&lt;p&gt;To calculate the damage, the attacker rolls a new set of dice — now the amount of dice depends on how deadly the
weapon is, how many strikes were rolled on the attack roll, and other factors. The defender does not roll dice this
time. Instead, each model has an armor value. A model with &quot;armor N&quot; takes 1 damage point for each N strikes on the
damage roll. For example, if the result or the roll is 5 strikes, and the defender has armor 2, the defender takes 2
damage points. If it was 6 strikes, it would take 3 damage points. Now, imagine that we want to calculate what&#x27;s the
distribution of damage that a defender will take against multiple attacks — in other words, what&#x27;s the chance that
it will take 0 damage, or 1 damage, or 2 damage, etc. Let&#x27;s see this in code:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; typing&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; Dict, List, Tuple&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt; prob_attack&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        attacks: List[Tuple[Roll, Roll]], defense_roll: Roll, armor:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;) -&amp;gt; Dice:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    damages&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; []&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    defense_dice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; get_dice(defense_roll)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span&gt; attack_roll, damage_roll&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; attacks:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        attack_dice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; get_dice(attack_roll)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        hits&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; (attack_dice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span&gt; defense_dice).max(Dice.from_empty())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;        if not&lt;&#x2F;span&gt;&lt;span&gt; hits:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;            continue&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        damage_dice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; Dice.sum(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            Dice.from_empty()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; @&lt;&#x2F;span&gt;&lt;span&gt; c&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;            if&lt;&#x2F;span&gt;&lt;span&gt; v&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;            else&lt;&#x2F;span&gt;&lt;span&gt; get_dice(Roll(action&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;damage_roll.action, power&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;damage_roll.power&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; v))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;                 @&lt;&#x2F;span&gt;&lt;span&gt; c&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;            for&lt;&#x2F;span&gt;&lt;span&gt; v, c&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; hits.items()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        damages.append(damage_dice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span&gt; armor&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;    if not&lt;&#x2F;span&gt;&lt;span&gt; damages:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        damages&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [Dice.from_empty()]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt; sum&lt;&#x2F;span&gt;&lt;span&gt;(damages)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The multiple attacks are represented as a &lt;code&gt;List[Tuple[Roll, Roll]]&lt;&#x2F;code&gt;, where each attack is a tuple with an
attack &lt;code&gt;Roll&lt;&#x2F;code&gt; and a damage &lt;code&gt;Roll&lt;&#x2F;code&gt;. Remember that a &lt;code&gt;Roll&lt;&#x2F;code&gt; is not a sample result of rolling dice, it&#x27;s the &lt;a href=&quot;#roll-definition&quot;&gt;amount of
dice rolled&lt;&#x2F;a&gt;.
A defender will roll the same number of defense dice against all the attacks, so we only need one &lt;code&gt;Roll&lt;&#x2F;code&gt; for that.&lt;&#x2F;p&gt;
&lt;p&gt;Computing the distribution of the defense dice is a simple &lt;code&gt;attack_dice = get_dice(attack_roll)&lt;&#x2F;code&gt;, using the function
shown before.&lt;&#x2F;p&gt;
&lt;p&gt;Computing the distribution of the difference between attack dice and defense dice is
&lt;code&gt;hits = (attack_dice - defense_dice).max(Dice.from_empty())&lt;&#x2F;code&gt;.
It&#x27;s important to calculate this distribution because, when calculating damage, the attacker rolls an extra power
dice for each strike on the difference between attack and defense dice.
You can see that when we iterate on each possible outcome of the attack&#x2F;defense roll with &lt;code&gt;for v, c in hits.items()&lt;&#x2F;code&gt;, we modify the original damage roll to add &lt;code&gt;v&lt;&#x2F;code&gt; to it:
&lt;code&gt;Roll(action=damage_roll.action, power=damage_roll.power + v)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Probably you got a bit confused with the &lt;code&gt;@&lt;&#x2F;code&gt; operator, which the
library &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Peilonrayz&#x2F;dice_stats&#x2F;blob&#x2F;47e4909ffca896121a15aedef06c7fae8533bd06&#x2F;src&#x2F;dice_stats&#x2F;dice.py#L193&quot;&gt;overloads&lt;&#x2F;a&gt;,
and it&#x27;s basically a nice looking replacement for the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;peilonrayz.github.io&#x2F;dice_stats&#x2F;public&#x2F;public.html?highlight=as_total_chance#dice_stats.Dice.as_total_chance&quot;&gt;
&lt;code&gt;Dice.as_total_chance&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
method. We are doing this because the outcome of the damage roll is conditioned by the probability of rolling that amount of strikes on the attack&#x2F;defense roll.&lt;&#x2F;p&gt;
&lt;p&gt;Well, that&#x27;s all I wanted to say. I think this is an awesome library and wanted to share it with you.&lt;&#x2F;p&gt;
&lt;p&gt;Happy rolling!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>I tried to opt-out of airlines selling my data to the government, and I am not sure if it worked</title>
        <published>2025-12-06T00:00:00+00:00</published>
        <updated>2025-12-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/opt-out-airlines-selling-data/"/>
        <id>https://sergiswriting.com/opt-out-airlines-selling-data/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/opt-out-airlines-selling-data/">&lt;p&gt;A few weeks ago, somebody posted on my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;floss.social&#x2F;@sergi&quot;&gt;Mastodon feed&lt;&#x2F;a&gt; a link to an old article
from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.404media.co&#x2F;&quot;&gt;404 Media&lt;&#x2F;a&gt;
called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;archive.is&#x2F;6FAao#selection-561.219-561.220&quot;&gt;How to Opt-Out of Airlines Selling Your Travel Data to the Government&lt;&#x2F;a&gt;
(I&#x27;m sorry kind stranger, I probably boosted your message but forgot to bookmark it to give you credit here). The
article explains that there is a data broker — the Airlines Reporting Corporation (ARC) — that sells flight data to
multiple parts of the Department of Homeland Security (DHS) and a host of other government agencies.&lt;&#x2F;p&gt;
&lt;p&gt;I was curious, decided to follow the process described in the post, and
e-mailed &lt;a href=&quot;mailto:privacy@arccorp.com&quot;&gt;privacy@arccorp.com&lt;&#x2F;a&gt;
with the subject &quot;Data deletion&quot; and the body:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hi,&lt;&#x2F;p&gt;
&lt;p&gt;I wish to delete my personal data held by ARC.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks,
Sergi&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I got the following reply the next day, which was surprisingly fast:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;We have received your recent request regarding the use of your personal information. We take your privacy seriously and
are committed to complying with applicable data protection laws.&lt;&#x2F;p&gt;
&lt;p&gt;Next Steps&lt;&#x2F;p&gt;
&lt;p&gt;We will process your request as soon as possible and, in any event, within the timeframes required by law.
In order to verify your identity, we need to know your full name, including middle name if applicable, as well as
the last four digits of any and all credit card number(s) that you used to purchase air travel. Please also provide
your address of residence. Any information collected for verification will be used solely for this purpose, and we
will promptly delete any such information once the verification process is complete.  If we do not receive this
information, we may not be able to process your request.
You will receive confirmation once your request has been fulfilled, along with information about any further
actions, if necessary.&lt;&#x2F;p&gt;
&lt;p&gt;Additional Information&lt;&#x2F;p&gt;
&lt;p&gt;If your request relates to specific types of data or processing (e.g., direct marketing, profiling, or third-party
sharing), please specify so we can address your request accurately.
There may be certain legal exceptions where we are required to retain or process some information, such as to
comply with legal obligations. If this applies, we will inform you accordingly.&lt;&#x2F;p&gt;
&lt;p&gt;If you have any further questions or would like to clarify your request, please reply to this message. Thank you for
reaching out regarding your privacy rights.&lt;&#x2F;p&gt;
&lt;p&gt;Best,
ARC Privacy&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Providing all this data felt a bit scary, but I guess they need to know who you are to know what to delete. So I replied
with my name and last name, four last digits of a couple of credit cards, and my address. Then, I
waited. Twelve days later — and probably would have been sooner if Thanksgiving wasn&#x27;t during that time — I
got this:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;We have received your request regarding the retention, use, and&#x2F;or deletion of your personal information. We take your
privacy seriously and are committed to complying with applicable data protection laws. We have repeatedly searched our
files and are unable to locate any of your personal information in our system. Any information you provided for
verification will not be used for any other purposes and will be deleted.&lt;&#x2F;p&gt;
&lt;p&gt;If you have any further questions or concerns, please reply to this message.&lt;&#x2F;p&gt;
&lt;p&gt;Best,
ARC Privacy&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Which was not what was I was expecting. If that&#x27;s true, I guess it&#x27;s good news. But I wonder if they botched the search
— having two last names separated with a space causes data input errors too frequently in the US — and that&#x27;s why
they couldn&#x27;t find my data.&lt;&#x2F;p&gt;
&lt;p&gt;If you give it a try and get a different outcome, I&#x27;m curious to hear out!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Arrow functions are overused</title>
        <published>2025-07-30T00:00:00+00:00</published>
        <updated>2025-07-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/arrow-functions-are-overused/"/>
        <id>https://sergiswriting.com/arrow-functions-are-overused/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/arrow-functions-are-overused/">&lt;p&gt;I&#x27;ve noticed something in online articles lately: arrow functions everywhere. It&#x27;s as if &lt;code&gt;function&lt;&#x2F;code&gt; became a dirty word
overnight, and every function declaration got replaced with &lt;code&gt;const myFunc = () =&amp;gt; {}&lt;&#x2F;code&gt;. While arrow functions are
undeniably useful, I think we&#x27;ve swung too far in the other direction.&lt;&#x2F;p&gt;
&lt;p&gt;Let me be clear upfront—there are absolutely cases where arrow functions shine and others where function declarations
are better. If you want a comprehensive breakdown of the differences, James Sinclair&#x27;s article
on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jrsinclair.com&#x2F;articles&#x2F;2025&#x2F;whats-the-difference-between-named-functions-and-arrow-functions&#x2F;&quot;&gt;the differences between ordinary functions and arrow functions&lt;&#x2F;a&gt;
is an excellent reference.&lt;&#x2F;p&gt;
&lt;p&gt;But when there isn&#x27;t a clear technical winner, many developers still default to arrow functions. The assumption seems to
be that they&#x27;re more concise. Let&#x27;s test that assumption.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-shorter-myth&quot;&gt;The &quot;shorter&quot; myth&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s a simple function declaration:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt; calculate&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;font-style: italic;&quot;&gt;  &#x2F;&#x2F; implementation&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And here&#x27;s the arrow function equivalent:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt; calculate&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; ()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;font-style: italic;&quot;&gt;  &#x2F;&#x2F; implementation&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Count the characters of the first line: 22 vs 35. The arrow function is actually &lt;em&gt;longer&lt;&#x2F;em&gt;. If we ignore the function name (&lt;code&gt;calculate&lt;&#x2F;code&gt;), it&#x27;s
13 vs 16 characters. The &quot;conciseness&quot; argument falls apart pretty quickly when you&#x27;re dealing with named functions.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;when-syntax-gets-in-the-way&quot;&gt;When syntax gets in the way&lt;&#x2F;h2&gt;
&lt;p&gt;I recently came across a perfect example of this in a TypeScript article
about &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;piccalil.li&#x2F;blog&#x2F;advanced-typescript-manipulation-features-for-the-real-world&#x2F;&quot;&gt;advanced type manipulation features&lt;&#x2F;a&gt;.
The author needed to demonstrate generic functions and wrote:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt; sendEvent&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt;E&lt;&#x2F;span&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt; EventName&lt;&#x2F;span&gt;&lt;span&gt;,&amp;gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  event&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt; E&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  payload&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt; HomeEvents&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt;E&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;font-style: italic;&quot;&gt; &#x2F;* ... *&#x2F;&lt;&#x2F;span&gt;&lt;span&gt; };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Notice that trailing comma after &lt;code&gt;EventName&lt;&#x2F;code&gt;? The author had to dedicate an entire sidebar to explain why it&#x27;s
there—because TypeScript and JSX syntax clash when using arrow functions with generics. The comma tells the parser it&#x27;s
looking at a generic type, not a JSX element.&lt;&#x2F;p&gt;
&lt;p&gt;The solution? Use a function declaration:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt; sendEvent&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt;E&lt;&#x2F;span&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt; EventName&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  event&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt; E&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  payload&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt; HomeEvents&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt;E&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;font-style: italic;&quot;&gt; &#x2F;* ... *&#x2F;&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Clean, clear, and no syntactic gymnastics required. The reader can focus on the actual TypeScript concepts being taught
instead of wrestling with parser ambiguities.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-modest-proposal&quot;&gt;A modest proposal&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s what I suggest: make function declarations your default choice. Reach for arrow functions when you have a
specific reason—when you need lexical &lt;code&gt;this&lt;&#x2F;code&gt;, when you&#x27;re writing a short callback, when you&#x27;re working in a functional
style.&lt;&#x2F;p&gt;
&lt;p&gt;But if you&#x27;re just writing a regular function that does some work and returns a result? Consider the humble &lt;code&gt;function&lt;&#x2F;code&gt;
keyword. Sometimes the old ways persist because they just work better.&lt;&#x2F;p&gt;
&lt;p&gt;The next time you type &lt;code&gt;const myFunction = () =&amp;gt; {&lt;&#x2F;code&gt;, pause for a second. Ask yourself: is there a compelling reason to
use an arrow function here, or am I just following a trend?&lt;&#x2F;p&gt;
&lt;p&gt;Happy coding!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Bringing back the scrollbars</title>
        <published>2025-03-30T00:00:00+00:00</published>
        <updated>2025-03-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/bring-back-scrollbars/"/>
        <id>https://sergiswriting.com/bring-back-scrollbars/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/bring-back-scrollbars/">&lt;p&gt;I don&#x27;t like the design pattern of hiding the scrollbars. I am not alone in this; I have frequently read articles
complaining about it, like this &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artemis.sh&#x2F;2023&#x2F;10&#x2F;12&#x2F;scrollbars.html&quot;&gt;blog post&lt;&#x2F;a&gt; that made it to
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=37864867&quot;&gt;Hacker News&lt;&#x2F;a&gt;,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;webdev&#x2F;comments&#x2F;ed1zu3&#x2F;is_anyone_else_annoyed_with_the_trend_for&#x2F;&quot;&gt;Reddit posts&lt;&#x2F;a&gt;, or even
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;answers.microsoft.com&#x2F;en-us&#x2F;msteams&#x2F;forum&#x2F;all&#x2F;when-are-stupidly-thin-scrollbars-going-to-be&#x2F;829b73fa-4383-4e45-a126-848e2ea70b4f&quot;&gt;posts on Microsoft forums&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There are different variations of this pattern. On some cases, the scrollbar is hidden, and it only shows up when hovering
the cursors over the area where the scrollbar is supposed to be. On other cases, it&#x27;s there, but very thin, and it only
becomes thicker — although not much nowadays — when
you hover over it. I think both cases are bad from a usability and accessibility perspective, and just another example
of today&#x27;s software industry where form triumphs usability.&lt;&#x2F;p&gt;
&lt;p&gt;Personally, I hate the ones that are hidden, as you lose the visual cue of how long the article&#x2F;content is, and
how much is left to scroll.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, on Firefox on Linux, this can be easily solved on the browser settings. Open the settings window, search for &quot;scrollbar&quot; on the search box,
and a checkbox with the text &lt;kbd&gt;&lt;samp&gt;Always show scrollbars&lt;&#x2F;samp&gt;&lt;&#x2F;kbd&gt; should show up. Enable it, and you are back to the good old times!&lt;&#x2F;p&gt;
&lt;p&gt;On Windows 11 it&#x27;s a bit different. You have to enable it at the OS level. Search for &quot;scroll bar&quot; on the search box of
the Windows task bar. The first option it suggests should say &lt;kbd&gt;&lt;samp&gt;Always show scroll bars &#x2F; System settings&lt;&#x2F;samp&gt;&lt;&#x2F;kbd&gt;.
Click on it, and it will open a window titled &lt;kbd&gt;&lt;samp&gt;Accessibility &amp;gt; Visual effects&lt;&#x2F;kbd&gt;&lt;&#x2F;samp&gt;. On it there should
be a checkbox with the text &lt;kbd&gt;&lt;samp&gt;Always show scrollbars&lt;&#x2F;kbd&gt;&lt;&#x2F;samp&gt; — you might have noticed they are inconsistent
with the spelling of &quot;scrollbar&quot;. Enable this option to get back to sanity.&lt;&#x2F;p&gt;
&lt;p&gt;Happy scrolling!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>How to fix a Pixel Watch stuck on Fastboot</title>
        <published>2025-03-08T00:00:00+00:00</published>
        <updated>2025-03-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/how-to-fix-pixel-watch-fastboot/"/>
        <id>https://sergiswriting.com/how-to-fix-pixel-watch-fastboot/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/how-to-fix-pixel-watch-fastboot/">&lt;p&gt;I am the owner of a Pixel Watch 2. During the past months, approximately once a week, the battery would suddenly deplete
very quickly. For example, I would charge it overnight, put it on around 8am, and when checking the time at 2pm I would find
out it was almost dead (or already dead). If I caught this happening before the device died, a reboot would fix it and
the watch would keep working as expected. To put it in perspective, a regular full charge lasts me more than 24 hours;
it can probably reach 36.&lt;&#x2F;p&gt;
&lt;p&gt;I tried looking online for a permanent solution but didn&#x27;t find anything. I thought about reaching out to Google for
support, but because of laziness, I never did. I ended up regretting that because when this behavior started it was probably
still under warranty.&lt;&#x2F;p&gt;
&lt;p&gt;Around the end of February, it happened again. I went to check the time and found out the watch was dead, only showing
the icon of empty battery. I plugged it in, and a few hours later, when I went to sleep, I grabbed it with the intention to
put it on and... I got greeted by what&#x27;s called the Fastboot screen.&lt;&#x2F;p&gt;
&lt;p&gt;The Fastboot screen is a menu with options like &lt;kbd&gt;&lt;samp&gt;Start&lt;&#x2F;samp&gt;&lt;&#x2F;kbd&gt;, &lt;kbd&gt;&lt;samp&gt;Power Off&lt;&#x2F;samp&gt;&lt;&#x2F;kbd&gt;, &lt;kbd&gt;&lt;samp&gt;Recovery Mode&lt;&#x2F;samp&gt;&lt;&#x2F;kbd&gt;, &lt;kbd&gt;&lt;samp&gt;Device Info&lt;&#x2F;samp&gt;&lt;&#x2F;kbd&gt;, etc. All the
options I tried that would reboot the device would bring me back to that menu. I was stuck there.&lt;&#x2F;p&gt;
&lt;p&gt;Inside the &lt;kbd&gt;&lt;samp&gt;Recovery Mode&lt;&#x2F;samp&gt;&lt;&#x2F;kbd&gt;, there is the option of &lt;kbd&gt;&lt;samp&gt;Wipe data&#x2F;Factory reset&lt;&#x2F;samp&gt;&lt;&#x2F;kbd&gt;. Even that did not fix the issue.
Interestingly, when I triggered that option, an error message would show up with:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;samp&gt;
ERROR: recovery: [libfs_mgr] Unable to enable ext4 verity on &#x2F;dev&#x2F;block&#x2F;bootdevice&#x2F;by-name&#x2F;metadata because 
&#x2F;system&#x2F;bin&#x2F;tune2fs is missing
&lt;p&gt;ERROR: recovery: Open failed: &#x2F;metadata&#x2F;ota: No such file or directory&lt;&#x2F;p&gt;
&lt;p&gt;Formatting &#x2F;data...
&lt;&#x2F;samp&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;After that, I could reboot the system, but that would load again the Fastboot.&lt;&#x2F;p&gt;
&lt;p&gt;I reached out to Google support. After trying to make me repeat the steps that I had just done, I explained to them I had already done
that and shared with them the error message I was getting. Their answer was short of &quot;Too bad. There is nothing you can
do then, and as the device is out of warranty we are not gonna give you a new one.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve had two Fitbit devices that died soon after the warranty expired, so with that now happening to the Pixel Watch
(warranty expired 2 months before this incident), I was fuming.&lt;&#x2F;p&gt;
&lt;p&gt;A few days later, I kept researching. I found out you can download images of the OS, officially from Google, and that you
can use the charger cable to flash the watch with them. Heck, what did I have to lose?&lt;&#x2F;p&gt;
&lt;p&gt;So that I did. Using the Watch app on my phone, I identified what build of the OS I was running before. Then I
downloaded the corresponding image from the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developers.google.com&#x2F;android&#x2F;ota-watch&quot;&gt;repository of full OTA images for Google Pixel Watch devices&lt;&#x2F;a&gt;. I am
not sure of the difference between OTA images and factory images, but all the posts I found online always pointed to the
OTA ones.&lt;&#x2F;p&gt;
&lt;p&gt;Next, I installed the Platform-Tools for Google Android SDK. Nowadays, I use Arch Linux, so I downloaded them from
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aur.archlinux.org&#x2F;packages&#x2F;android-sdk-platform-tools&quot;&gt;AUR&lt;&#x2F;a&gt;. This package includes &lt;code&gt;adb&lt;&#x2F;code&gt;, aka Android Debug
Bridge, which allows you to communicate with Android devices.&lt;&#x2F;p&gt;
&lt;p&gt;Then, on the watch, using the Fastboot menu, I went to &lt;kbd&gt;&lt;samp&gt;Recovery mode&lt;&#x2F;samp&gt;&lt;&#x2F;kbd&gt;, and triggered the option &lt;kbd&gt;&lt;samp&gt;Apply update from ADB&lt;&#x2F;samp&gt;&lt;&#x2F;kbd&gt;.
Then, I plugged the watch into the computer with the charger cable and checked that the device was recognized by
executing:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;adb&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; devices&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This listed my watch, so I started flashing the image with:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;adb&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; sideload aurora-ota-aw2a.241105.012-a7c5bc2d.zip&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That showed me a progress report. This part was a little nerve-breaking because the charger attaches to the watch
mostly with magnets, not a physical attachment, so it&#x27;s very easy to unplug it, and I was worried that a disconnection
mid-flashing could completely brick the device.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, none of that happened. The flashing finished, the watch rebooted, and it was as good as new! After
pairing it again with my phone it downloaded the latest online backup and all my apps and settings were re-applied.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m very happy that I could fix it, but at the same time, I am deeply disappointed that Google support did not inform me
of this option. Either they lied by telling me there was nothing I could do about it, or they are insufficiently trained
to do their job.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Ode to the Kindle Keyboard (Kindle 3)</title>
        <published>2025-01-18T00:00:00+00:00</published>
        <updated>2025-01-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/ode-to-the-kindle-keyboard/"/>
        <id>https://sergiswriting.com/ode-to-the-kindle-keyboard/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/ode-to-the-kindle-keyboard/">&lt;p&gt;In 2010, I got a Kindle Keyboard (aka Kindle 3) as a Christmas gift from my parents. It&#x27;s still my only ebook reader,
and it&#x27;s probably the oldest piece of technology I regularly use.&lt;&#x2F;p&gt;
&lt;figure&gt;



&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 2; --d: 3;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard.6250ec228b5f0608.jpg&quot; alt=&quot;Full body photography of the Kindle Keyboard.&quot;&gt;

&lt;&#x2F;div&gt;


&lt;figcaption&gt;It&#x27;s 14 years old and still works!&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;the-specs&quot;&gt;The specs&lt;&#x2F;h2&gt;
&lt;p&gt;The particular model I own is the one with only Wi-Fi. There was a more expensive option with 3G but at the time
— and still nowadays — I saw no reason for that feature.&lt;&#x2F;p&gt;
&lt;p&gt;According to Wikipedia, this model weighs 240 g (8.5 oz on freedom units), with a total size of 190 × 120 × 8.6 mm
(7.5 × 4.8 × 0.34 in).&lt;&#x2F;p&gt;
&lt;p&gt;It features a 6-inch screen with a resolution of 600 × 800 pixels, for a density of 167 PPI. It rocks a 16-level
grayscale palette.&lt;&#x2F;p&gt;
&lt;p&gt;It has 3GB of storage that I doubt I&#x27;ll ever fill, a USB 2.0 Micro-B port for charging, speakers, and a 3.5mm headphone.
These audio outputs are for a text-to-speech feature that I think I have never used.&lt;&#x2F;p&gt;
&lt;p&gt;And, probably the most shocking feature for young generations, it has a small keyboard. Look at this cutie:&lt;&#x2F;p&gt;
&lt;figure&gt;



&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 4; --d: 3;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-keyboard.d6fb1a5d7fd363f2.jpg&quot; alt=&quot;Close up of the keyboard.&quot;&gt;

&lt;&#x2F;div&gt;


&lt;figcaption&gt;A beautiful clicky keyboard with real, physical buttons.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Having that keyboard so readily available emphasized annotating your ebooks. I very rarely do that, but if I did I would
love to do it on this device! You can sync these annotations with your Amazon account, so I guess that if you get a new
Kindle device you can port them over.&lt;&#x2F;p&gt;
&lt;p&gt;You can also highlight text. An interesting feature — that can be disabled — is to see text that is frequently
highlighted by other users — this only works on books bought through Amazon —. And you can decide if you want
to share your highlights or not.&lt;&#x2F;p&gt;
&lt;p&gt;If you want an even more simplified marking, you can bookmark pages.&lt;&#x2F;p&gt;
&lt;figure&gt;



&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-highlight-note-bookmark.465aef1098f63c66.jpg&quot; alt=&quot;Close up of the screen showing a highlight, annotation and bookmark.&quot;&gt;

&lt;&#x2F;div&gt;


&lt;figcaption&gt;You can list all your notes, highlights and bookmarks for a given book.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;All this data — for all the books — can be found on the plain text file &lt;code&gt;My Clippings.txt&lt;&#x2F;code&gt; on the device.
Yeah, if you connect it to a computer it will mount like a standard USB drive.&lt;&#x2F;p&gt;
&lt;p&gt;This is the plain text representation of the highlight, annotation, and bookmark shown in the image above:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;==========&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Dune (Frank Herbert)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;- Highlight on Page 2 | Loc. 142-45  | Added on Saturday, January 14, 2023, 04:25 AM&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;By the half-light of a suspensor lamp, dimmed and hanging near the floor, the awakened boy could see a bulky female shape at his door, standing one step ahead of his mother. The old woman was a witch shadow—hair like matted spiderwebs, hooded ’round darkness of features, eyes like glittering jewels. &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;==========&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Dune (Frank Herbert)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;- Note on Page 4 | Loc. 146  | Added on Saturday, January 14, 2023, 04:31 AM&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;what a profound statement&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;==========&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Dune (Frank Herbert)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;- Bookmark on Page 4 | Loc. 150  | Added on Saturday, January 14, 2023, 04:33 AM&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;==========&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;my-experience&quot;&gt;My experience&lt;&#x2F;h2&gt;
&lt;p&gt;I love this device. It has been with me for 14 years now.&lt;&#x2F;p&gt;
&lt;p&gt;Yes, I am not a heavy user, but I can still load and read ebooks into it, and it works with Amazon ebooks or standard
EPUB and .mobi files. Even PDFs if you feel like it, although these are a bit of a pain as they are formatted for big
screens, and they need a lot of zooming and panning around. I tried a couple of times to read a manga where each page was
an image, but the screen was a tad too small for it, and having to zoom and pan bothered me too much.&lt;&#x2F;p&gt;
&lt;p&gt;If I buy a book via Amazon it will automatically sync it and add it to the device. If I get the file from somewhere
else, I usually use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;calibre-ebook.com&#x2F;&quot;&gt;Calibre&lt;&#x2F;a&gt; to load it. If I feel like it, I can send the file as an
attachment to a special e-mail account Amazon provided for my device, and it will automagically add it to the device
over Wi-Fi.&lt;&#x2F;p&gt;
&lt;p&gt;I only turn on the Wi-Fi when I want to add new books via Amazon services, otherwise, I keep it off. With that, the
battery lasts long enough so that I can read a book from start to end without needing to charge. I&#x27;m not sure how long
it actually lasts because I rarely read two ebooks back to back, but heck, we plug in our phones every night and nobody
beats an eye. This 14-year-old device lasts between charges way longer even with heavy use!&lt;&#x2F;p&gt;
&lt;p&gt;The supported Wi-Fi is 802.11bg at 2.4 GHz, and fortunately, that has not been phased out. And for the few MBs an ebook
takes, transfer speed is not an issue. And when using Calibre I use USB anyway.&lt;&#x2F;p&gt;
&lt;p&gt;All the clicky buttons still work, even the ones for turning pages which are the ones I&#x27;ve used the most. The &quot;next
page&quot; button on the right side feels softer, but that&#x27;s it. And yes, I said &quot;on the right side&quot;, because the same
buttons are also on the left side for easy page change if you are holding it with the left hand.&lt;&#x2F;p&gt;
&lt;p&gt;I said I barely use the keyboard, but the extra space and weight do not bother me. It actually serves as a nice extra
surface to comfortably hold it.&lt;&#x2F;p&gt;
&lt;p&gt;Turning a page takes about 1 second, and that&#x27;s good enough for me. The resolution provides very legible text and B&amp;amp;W
graphics.&lt;&#x2F;p&gt;
&lt;figure&gt;



&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-screen.4b1edd2edeb7bc35.jpg&quot; alt=&quot;Close up of the screen.&quot;&gt;

&lt;&#x2F;div&gt;


&lt;figcaption&gt;This is perfectly readable. Any blur is because the picture is crap, in reality the text is very crisp.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;I can customize between 3 different typefaces, 8 different font sizes, 3 different line spacings, and 3 different words
per line. Look at this:&lt;&#x2F;p&gt;
&lt;figure&gt;



&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 1; --d: 1;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-screen-configuration.217fb38d2437c9df.jpg&quot; alt=&quot;Close up of the screen showing the screen configuration options.&quot;&gt;

&lt;&#x2F;div&gt;


&lt;figcaption&gt;The menu to configure how text is rendered.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;the-lock-screens&quot;&gt;The lock screens&lt;&#x2F;h2&gt;
&lt;p&gt;I found the lock screen very amusing. Every time you lock it, the screen switches to the portrait of an author or a
sketch. Remember that an and electronic ink screen only uses energy when refreshing; keeping content frozen uses zero
battery. These are the different lock screens it rotates through:&lt;&#x2F;p&gt;
&lt;figure&gt;



&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-00.4d767c6d86377b69.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of the silhouette of a person reading a book under a tree on a hilly landscape. It has text with the definition of the word kindle.&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-01.ccbb56b3f3841144.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of seven birds on a branch.&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-02.1d561851f51a8d62.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of Virginia Woolf.&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-03.a0b8932386a25702.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of Jules Verne.&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-04.cf6199e7df965402.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of some kind of Middle Ages book page.&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-05.a39ae9ab4829e30f.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of Jane Austen.&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-06.9685e884cdf91f21.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of Ralph Ellison.&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-07.685b0b055620853e.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of Harriet Beecher Stowe&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-08.52fd7f7646cd0f95.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of Emily Dickinson.&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-09.1b18be8c623fe1cb.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of an ancient start chart called Ophiucus &amp;amp; Serpens&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-10.8c86937e00014159.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of two people using some unknown giant device (maybe it measures celestial distances or angles).&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-11.18a95c617d5f5030.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of Mark Twain.&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-12.4461dc619ca65ec3.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of some Roman or middle age&amp;#x27;s aristocrat woman holding a book with latin text.&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-13.99bcc19bd36944a3.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of 18 fishes of different size and shape.&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-14.340f3a150c54e9e7.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of John Steinbeck.&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-15.3057c45eeb97475e.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of a man from an unknown old time (maybe victorian?) writing a note.&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-16.e434606ee5bd6323.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of a man working or writing on a workshop. There is a dog and what seems a lion sleeping on the floor. It seems to be on some ancient time.&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-17.48ddc34abd919bcc.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of a Middle Ages man with a heraldic shield in the back.&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-18.ec7c8bc15e22015e.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of Charlotte Bronte.&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-19.f8584f457aaada59.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of a building (maybe Roman?) showing the top view sketch and a slice of the side view.&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-20.2b0fde392c91b16a.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of Agatha Christie.&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-21.7b7c90cbcafc2c24.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of Alexandre Dumas.&quot;&gt;

&lt;&#x2F;div&gt;





&lt;div class=&quot;[ frame ]&quot; style=&quot;--n: 3; --d: 4;&quot;&gt;

    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;kindle-keyboard-lock-screen-22.86983d503ace8a35.jpg&quot; alt=&quot;Close up of the screen showing a lock screen. It has a drawing of some kind of cyphering machine, with rows of wheels with numbers. It looks ancient, like an invention of Leonardo Da Vinci.&quot;&gt;

&lt;&#x2F;div&gt;


&lt;figcaption&gt;These are all the possible lock screens in order of rotation.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;jailbreaking&quot;&gt;Jailbreaking&lt;&#x2F;h2&gt;
&lt;p&gt;The Kindle Keyboard can be jailbroken to customize it or extend its capabilities, but I never felt the need to do it, so
I didn&#x27;t. I have nothing else to say about it ¯\_(ツ)_&#x2F;¯.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;i-m-not-buying-a-new-model&quot;&gt;I&#x27;m not buying a new model&lt;&#x2F;h2&gt;
&lt;p&gt;I don&#x27;t see why I should. Everything works. I can read ebooks bought from Amazon or any other platform that offers EPUBs
or compatible formats. It performs this function well; I have no complaints about legibility or performance. The only feature
from new models I might have missed at some point is the front-lit screen for reading in low light, but it&#x27;s rare enough
for me that it&#x27;s not a deal-breaker. And I bet that newer models are full of tracking technology.&lt;&#x2F;p&gt;
&lt;p&gt;That makes me wonder how often other people replace their ebook readers, and why. Definitely more frequently than I do,
otherwise I don&#x27;t see how manufacturers could keep up with the high pace of releasing new models. Just looking at the
Kindle, I see 5 models were released in 2024, 2 models in 2022, 2 models in 2021, and 2 models in 2019. That&#x27;s 11 new devices
launched during the last 5 years!&lt;&#x2F;p&gt;
&lt;p&gt;It makes me sad that I don&#x27;t have more devices around me that are this old — yes, I am not counting old stuff
inside a storage box that never gets to see the light of day because I don&#x27;t have real use for it.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Refactoring my homepage</title>
        <published>2024-12-26T00:00:00+00:00</published>
        <updated>2024-12-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/homepage-refactoring/"/>
        <id>https://sergiswriting.com/homepage-refactoring/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/homepage-refactoring/">&lt;p&gt;In &lt;a href=&quot;https:&#x2F;&#x2F;sergiswriting.com&#x2F;excited-for-css&#x2F;&quot;&gt;my previous post&lt;&#x2F;a&gt;, I said I wanted to rebuild my blog using the principles of
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cube.fyi&#x2F;&quot;&gt;CUBE CSS&lt;&#x2F;a&gt; and &lt;cite&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;every-layout.dev&#x2F;&quot;&gt;Every Layout&lt;&#x2F;a&gt;&lt;&#x2F;cite&gt;. But I decided to start with
something smaller, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.cub3.net&#x2F;&quot;&gt;my homepage&lt;&#x2F;a&gt;, and see how that turns out.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-it-looks&quot;&gt;How it looks&lt;&#x2F;h2&gt;
&lt;p&gt;First, let&#x27;s have a quick look at the &quot;before and after&quot; of the rendered site:&lt;&#x2F;p&gt;
&lt;figure&gt;




    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;old-homepage.fabdca34f5affdd5.png&quot; alt=&quot;Screenshot of the old homepage rendered at 1920x1080.&quot;&gt;


&lt;figcaption&gt;Old homepage rendered at 1920x1080.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;figure&gt;




    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;new-homepage.6bdeedecb2a190f2.png&quot; alt=&quot;Screenshot of the new homepage rendered at 1920x1080.&quot;&gt;


&lt;figcaption&gt;New homepage rendered at 1920x1080.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;They look very similar, though not identical, but this is fine. I was not aiming to exactly reproduce the same layout,
but something similar that I liked. The change you probably noticed first is that now the content is centered vertically,
and that was an intentional change. You might also notice a bigger contrast on the new website, with the default font
color being darker. I decided to change that as part of declaring the site colors as global variables.&lt;&#x2F;p&gt;
&lt;p&gt;On small resolutions, they also look nearly identical:&lt;&#x2F;p&gt;
&lt;figure&gt;




    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;old-homepage-phone.f1fbbd8ba7640061.png&quot; alt=&quot;Screenshot of the old homepage rendered at 375x667.&quot;&gt;


&lt;figcaption&gt;Old homepage rendered at 375x667, resolution that Firefox labels as &quot;iPhone SE 2nd gen iOS 14.6&quot;.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;figure&gt;




    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;new-homepage-phone.0a5c23b9a9b78724.png&quot; alt=&quot;Screenshot of the new homepage rendered at 375x667.&quot;&gt;


&lt;figcaption&gt;New homepage rendered at 375x667, resolution that Firefox labels as &quot;iPhone SE 2nd gen iOS 14.6&quot;.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;using-variables&quot;&gt;Using variables&lt;&#x2F;h2&gt;
&lt;p&gt;A good &quot;developer quality of life&quot; improvement was using
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;CSS&#x2F;Using_CSS_custom_properties&quot;&gt;CSS variables&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I defined them for all the colors used on the page, plus
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;every-layout.dev&#x2F;rudiments&#x2F;modular-scale&#x2F;&quot;&gt;ratios for harmonic sizing&lt;&#x2F;a&gt;. Before, hexadecimal was all over the place,
alongside font sizes declared in rem. Now all these properties reference variables, making it easier to keep them
consistent. As mentioned before, I used the opportunity to tweak the color values to improve the contrast using a contrast
calculator. I can&#x27;t remember which one I used, but you can find many online that will tell you if the ratio conforms to
the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.w3.org&#x2F;WAI&#x2F;standards-guidelines&#x2F;wcag&#x2F;&quot;&gt;WCAG&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;While working on font sizes, I realized that I did not have a maximum line length. On narrow screens, it would just
wrap and break into new lines to not exceed the viewport&#x27;s width, but on wide screens, the content would be as wide
as the longest line. I was fortunate that none of the existing lines were too long, so it didn&#x27;t look too bad, but that
has now changed to a maximum of 65 characters — yes, I discovered that
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;every-layout.dev&#x2F;rudiments&#x2F;axioms&#x2F;&quot;&gt;character is a unit&lt;&#x2F;a&gt;! — for optimal legibility.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;using-a-css-reset&quot;&gt;Using a CSS reset&lt;&#x2F;h2&gt;
&lt;p&gt;I did not have a CSS reset before, and while that seemed to be fine, I was risking the layout being off on some
current or future browsers because of some unexpected defaults. I stole the one from
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;piccalil.li&#x2F;blog&#x2F;a-more-modern-css-reset&#x2F;&quot;&gt;Piccalilli&lt;&#x2F;a&gt;, using only the parts that would apply to the HTML
elements on my site. If in the future I add new elements I might have to expand that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-css-feels-more-clean-but-there-is-way-more-of-it-than-before&quot;&gt;The CSS feels more clean, but there is way more of it than before&lt;&#x2F;h2&gt;
&lt;p&gt;I implemented the patterns for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;every-layout.dev&#x2F;layouts&#x2F;cover&#x2F;&quot;&gt;Cover&lt;&#x2F;a&gt;,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;every-layout.dev&#x2F;layouts&#x2F;center&#x2F;&quot;&gt;Center&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;every-layout.dev&#x2F;layouts&#x2F;center&#x2F;&quot;&gt;Center with centered text&lt;&#x2F;a&gt; and
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;every-layout.dev&#x2F;layouts&#x2F;stack&#x2F;&quot;&gt;Stack&lt;&#x2F;a&gt;. The HTML is clean and fully semantic with just a few &lt;code&gt;div&lt;&#x2F;code&gt;s only for
layout purposes:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;cover&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;center&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;stack&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;            &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;header&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;stack center-text&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;h1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Sergi Pons&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt;&amp;amp;nbsp;&lt;&#x2F;span&gt;&lt;span&gt;Freixes&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;h1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;h2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Software&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt;&amp;amp;nbsp;&lt;&#x2F;span&gt;&lt;span&gt;Engineer&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt;&amp;amp;nbsp;&lt;&#x2F;span&gt;&lt;span&gt;&amp;amp; Solutions&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt;&amp;amp;nbsp;&lt;&#x2F;span&gt;&lt;span&gt;Architect&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;h2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;            &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;header&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;            &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;center&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;section&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;stack&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Most of my open source contributions can be found on &lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                            href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;github.com&#x2F;sponsfreixes&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; target&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;_blank&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                            rel&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;author external&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;GitHub&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;. Highlights:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Creator of &lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;github.com&#x2F;sponsfreixes&#x2F;jinja2-fragments&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Jinja2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                            Fragments&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                        &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                            Creator of &lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;github.com&#x2F;sponsfreixes&#x2F;htmx-flask&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;htmx-flask&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                            and maintainer of &lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;github.com&#x2F;edmondchuc&#x2F;flask-htmx&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Flask-htmx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                            after the projects got merged.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                        &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                            Maintainer of &lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                                href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;github.com&#x2F;MongoEngine&#x2F;marshmallow-mongoengine&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Marshmallow-Mongoengine&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                        &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                        If you are interested on my career, check out my &lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;cv.html&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;CV&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; or&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                        my &lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;sergiponsfreixes&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; target&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;_blank&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                              rel&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;author external&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;LinkedIn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Contact me on &lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; rel&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;me&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; target&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;_blank&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;floss.social&#x2F;@sergi&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Mastodon&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;x.com&#x2F;sponsfreixes&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; target&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;_blank&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; rel&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;author external&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;X&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                        or at &lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;mailto:sergi@cub3.net&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; rel&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;author&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;sergi@cub3.net&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Oh, and I have a &lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;sergiswriting.com&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt; rel&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;author external&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;blog&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;!&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;                &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;section&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;            &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;        &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #93A1A1;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With these reusable layout classes, I can picture in my head how the different blocks will be
approximately rendered. The CSS for these classes makes it so that there is way more CSS than before where I just had
some sprinkles of flexbox spread around. But now I feel that I can expand the site with more confidence because I have
these reusable blocks to work with.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Was this change needed? No. The previous site had a decent layout, and I expect that I will very rarely add new content
to it.&lt;&#x2F;p&gt;
&lt;p&gt;But this was a learning exercise. Doing these changes felt good. Somehow, it feels like it&#x27;s a more robust composition, easier
to reason about. Both the HTML and CSS are clean and legible. In summary, the vibes are good enough that I will apply
it to &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;cub3.net&#x2F;cv.html&quot;&gt;my CV page&lt;&#x2F;a&gt; next. After that, I will tackle this blog itself, where I expect the gains
will be more quantifiable rather than just about &quot;a good feeling&quot;.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>I feel excited about writing CSS</title>
        <published>2024-09-23T00:00:00+00:00</published>
        <updated>2024-09-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/excited-for-css/"/>
        <id>https://sergiswriting.com/excited-for-css/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/excited-for-css/">&lt;p&gt;I don&#x27;t feel fully satisfied with the CSS of this blog. I handcrafted it from scratch, and while overall I like it,
there are a few quirks here and there (like the spacing of list elements on some edge cases) that bother me. At the
beginning of writing the CSS, it was feeling good because I was
mostly &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;CSS&#x2F;Type_selectors&quot;&gt;targeting elements by type&lt;&#x2F;a&gt;. However, as I
needed to add exceptions and customizations, I began
using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;CSS&#x2F;Child_combinator&quot;&gt;child combinators&lt;&#x2F;a&gt; with the types. Eventually,
that was not enough, and ended also adding
some &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;CSS&#x2F;Class_selectors&quot;&gt;classes&lt;&#x2F;a&gt; and even targeting
by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;CSS&#x2F;ID_selectors&quot;&gt;ID&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This approach isn&#x27;t inherently bad, but it made me realize that I didn&#x27;t have a strong guideline for what type of
selector to use. I was trying to target by type as much as possible because somehow it seemed to help to enforce
using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Glossary&#x2F;Semantics&quot;&gt;semantic HTML&lt;&#x2F;a&gt;, which is
a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.tracylum.com&#x2F;blog&#x2F;2015-12-21-the-benefits-of-semantic-html&#x2F;&quot;&gt;good&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.codecademy.com&#x2F;resources&#x2F;blog&#x2F;semantic-html&#x2F;&quot;&gt;thing&lt;&#x2F;a&gt;.
But that was it. All the other decisions seem too ad hoc.&lt;&#x2F;p&gt;
&lt;p&gt;Something similar is happening at work. We don&#x27;t have any guidelines for writing CSS or HTML; we only have them for
JavaScript. Additionally, no one on the team is a web design expert who could teach the rest of us. We have been
using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;primeng.org&#x2F;&quot;&gt;PrimeNG&lt;&#x2F;a&gt; on most projects and dabbled a bit with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tailwindcss.com&#x2F;&quot;&gt;Tailwind CSS&lt;&#x2F;a&gt;
on a few others, but the former just gives you some pre-built components and the latter a set of utility classes (which
I have a gut feeling &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;heydonworks.com&#x2F;article&#x2F;what-is-utility-first-css&#x2F;&quot;&gt;is not the right way to go&lt;&#x2F;a&gt;). Neither
of these resources teaches you about layouts or how to organize CSS properly.&lt;&#x2F;p&gt;
&lt;p&gt;I started doing some research and discovered two resources that I read during my last vacation. They not only seemed to
be what I was looking for, but their approach and philosophy made me feel excited about writing CSS!&lt;&#x2F;p&gt;
&lt;p&gt;The first resource is the website about &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cube.fyi&#x2F;&quot;&gt;CUBE CSS&lt;&#x2F;a&gt;. Citing the homepage:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;CUBE CSS is a CSS methodology that’s orientated towards simplicity, pragmatism and consistency. It’s designed to work &lt;em&gt;with&lt;&#x2F;em&gt; the medium that you’re working in—often the browser—rather than &lt;em&gt;against&lt;&#x2F;em&gt; it.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Among other things, CUBE CSS proposes a methodology for organizing your CSS. I&#x27;m not going to summarize or try to
explain what it is. The website itself does a better job than I would. And if you don&#x27;t feel like reading, there
are &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cube.fyi&#x2F;examples.html#talks-videos-and-interviews&quot;&gt;a few videos covering the same content&lt;&#x2F;a&gt;. What I will do
is highlight &lt;em&gt;why&lt;&#x2F;em&gt; I like it and cite some parts that resonate with me:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;It relies on the browser to do its job, in opposite of replicating&#x2F;duplicating its built-in functionality like many
try to do with a JavaScript-heavy approach to web development. It promotes the idea that the web is fluid and rigid
layouts are destined to break:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;The browser is hinted, rather micro-managed to do what it knows best in the context that it finds itself in.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;It doesn&#x27;t force a library or tool, it&#x27;s a way of thinking about vanilla CSS:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;CUBE CSS is completely tool agnostic because it is more of a thinking and organisational methodology, rather than a tooling methodology.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;It acknowledges that CSS is not easy. It shouldn&#x27;t be treated as a second-class citizen of the web, and to properly
build good designs one must learn it properly, not avoid or hide it:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;CSS is an incredibly complex programming language because a lot of knowledge is in how it works—not just how to write the syntax—which is understandably difficult when someone is used to other programming languages breaking and&#x2F;or throwing errors, like JavaScript.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;While reading about CUBE CSS, I found many references to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;every-layout.dev&#x2F;&quot;&gt;Every Layout&lt;&#x2F;a&gt;, and I realized it
was a book I had seen often praised on social media and blog posts. This is the second resource I wanted to talk about.&lt;&#x2F;p&gt;
&lt;p&gt;I loved it. This book is not a set of CSS tricks or a bunch of pre-packaged code ready to be copy-pasted without
thinking about it. It&#x27;s a philosophy, a way of creating resilient layouts and making you write good and powerful CSS. As
it is focused on layouts, it&#x27;s a great resource to use when building the composition layer of CUBE CSS for your site. It
assumes you already know the basics of CSS, so it&#x27;s not for learning CSS from scratch. But if you have some basic
notions you will have no issues following it.&lt;&#x2F;p&gt;
&lt;p&gt;I strongly recommend buying it if you are a web developer who does any frontend at all. Like before, I am going to
mention some ideas and citations that I found very attractive and made me think &quot;Writing CSS is fun!&quot;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;It takes accessibility seriously. It is treated as any other design consideration, not as an afterthought.&lt;&#x2F;li&gt;
&lt;li&gt;When coming up with sizes&#x2F;proportions, it relies on delegating to an algorithm how to determine the value rather than
hard coding it as a px-based width.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;To design “adaptable pages” (Allsopp’s term), we must relinquish control to the algorithms (like text wrapping) browsers use to lay out web pages automatically. But that’s not to say there’s no place for influencing layout. Think of yourself as the browser’s mentor, rather than its micro-manager.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;The layouts of Every Layout do not explore or prescribe styles for simple elements; that is for you to decide. It is the imbrication of simple elements into composite layouts that we are interested in here.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;...it’s better to provide suggestions rather than diktats about the way the visual design is laid out. An overuse of @media breakpoints can easily come about when we try to fix designs to different contexts and devices. By only suggesting to the browser how it should arrange our layout boxes, we move from creating multiple layouts to single quantum layouts existing simultaneously in different states.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;With all that said, I&#x27;m planning to rewrite the CSS of this blog and probably of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.cub3.net&#x2F;&quot;&gt;my homepage&lt;&#x2F;a&gt;
using these patterns. I&#x27;ll possibly write up how my experience goes after the fact. Wish me luck!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Do not reinvent the wheel</title>
        <published>2024-09-18T00:00:00+00:00</published>
        <updated>2024-09-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/do-not-reinvent-the-wheel/"/>
        <id>https://sergiswriting.com/do-not-reinvent-the-wheel/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/do-not-reinvent-the-wheel/">&lt;p&gt;Python&#x27;s standard library is awesome. Just skim through the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;index.html&quot;&gt;list of available packages and modules&lt;&#x2F;a&gt;. I bet that there is something in there that you didn&#x27;t know existed or that you have forgotten about it. From time to time I like to go through this list or check the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;whatsnew&#x2F;changelog.html&quot;&gt;Python release notes&lt;&#x2F;a&gt; to see if there is any addition that can make my life easier.&lt;&#x2F;p&gt;
&lt;p&gt;It is not uncommon to forget about it and implement some functionality that already exists from scratch, and it&#x27;s probably implemented in a better way. In other words, it&#x27;s easy to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Reinventing_the_wheel&quot;&gt;reimplement the wheel&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;A clear example of that is a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;dassencio.org&#x2F;83&quot;&gt;blog post&lt;&#x2F;a&gt; I stumbled into a few weeks ago, which describes a way of checking if an IP belongs to a subnetwork. The solution on that blog post takes 83 lines of code (31 are docstrings), split into three functions: a main function and two auxiliary. With the help of the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;ipaddress.html&quot;&gt;&lt;code&gt;ipaddress&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; package from the standard library, it can be reduced to only 20 lines (9 are docstrings) and a single function, as the code is short enough for that:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; ipaddress&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt; ip_in_subnetwork&lt;&#x2F;span&gt;&lt;span&gt;(ip_address, subnetwork):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;    Returns True if the provided IP address belongs to the&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;    subnetwork (specified in CIDR notation); otherwise, returns&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;    False. Both arguments should be strings.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;    Supports both IPv4 (e.g., &amp;#39;192.168.1.1&amp;#39; and &amp;#39;192.168.1.0&#x2F;24&amp;#39;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;    and IPv6 (e.g., &amp;#39;2a02:a448:ddb0::&amp;#39; and &amp;#39;2a02:a448:ddb0::&#x2F;44&amp;#39;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;    addresses and subnetworks.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    address&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; ipaddress.ip_address(ip_address)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    network&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; ipaddress.ip_network(subnetwork)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span&gt; address.version&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span&gt; network.version:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;        raise&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt; ValueError&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;#39;Incompatible IP versions&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; address&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; network&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I want to make it clear that my intention is not to dunk on that other blog post, but I thought it was a real and good example of what can happen to anybody who doesn&#x27;t know that what they need already exists. Notice that the author leverages two other packages from the standard library!&lt;&#x2F;p&gt;
&lt;p&gt;In all fairness, their solution works on both Python 2 and 3, while mine is 3.3+ only. But, notice how for readers interested in a Python 3-only solution, they recommend using the 3rd party package &lt;code&gt;netaddr&lt;&#x2F;code&gt;, which makes me think the author just didn&#x27;t know of the existence of &lt;code&gt;ipaddress&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Happy coding!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>SQLAlchemy relationship with five tables</title>
        <published>2024-08-05T00:00:00+00:00</published>
        <updated>2024-08-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/sqalchemy-relationship-with-five-tables/"/>
        <id>https://sergiswriting.com/sqalchemy-relationship-with-five-tables/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/sqalchemy-relationship-with-five-tables/">&lt;p&gt;I had to create a relationship that needed to combine five different tables on a project I was working on. The SQLAlchemy documentation about &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.sqlalchemy.org&#x2F;en&#x2F;20&#x2F;orm&#x2F;join_conditions.html#composite-secondary-joins&quot;&gt;composite secondary joins&lt;&#x2F;a&gt; has an example for combining four tables, and in this blog post, I am extending it to five.&lt;&#x2F;p&gt;
&lt;p&gt;We start with a &lt;code&gt;Company&lt;&#x2F;code&gt; model. That model represents, well, a company. That company will have users, which we represent with the &lt;code&gt;User&lt;&#x2F;code&gt; model with a foreign key pointing to their company.&lt;&#x2F;p&gt;
&lt;p&gt;In addition to that, we have &lt;code&gt;Product&lt;&#x2F;code&gt; model that represents some sort of service that one or more companies might use. We model this with a &lt;code&gt;Product&lt;&#x2F;code&gt; model and a &lt;code&gt;ProductSubscription&lt;&#x2F;code&gt; model with foreign keys to a product and company. Each user can configure how they use a product their company is subscribed to. This is represented with a &lt;code&gt;ProductUserConfiguration&lt;&#x2F;code&gt; model that contains foreign keys to the user and to the product.&lt;&#x2F;p&gt;
&lt;p&gt;Putting this all together, the models look like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt; Company&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6C71C4;&quot;&gt;Base&lt;&#x2F;span&gt;&lt;span&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    __tablename__&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; &amp;quot;company&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;    id&lt;&#x2F;span&gt;&lt;span&gt;: Mapped[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mapped_column(primary_key&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B58900;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    name: Mapped[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mapped_column(String(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt;64&lt;&#x2F;span&gt;&lt;span&gt;), nullable&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B58900;&quot;&gt;False&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt; User&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6C71C4;&quot;&gt;Base&lt;&#x2F;span&gt;&lt;span&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    __tablename__&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; &amp;quot;user&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;    id&lt;&#x2F;span&gt;&lt;span&gt;: Mapped[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mapped_column(primary_key&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B58900;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    name: Mapped[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mapped_column(String(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt;64&lt;&#x2F;span&gt;&lt;span&gt;), nullable&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B58900;&quot;&gt;False&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    company_id: Mapped[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mapped_column(ForeignKey(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;company.id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt; Product&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6C71C4;&quot;&gt;Base&lt;&#x2F;span&gt;&lt;span&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    __tablename__&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; &amp;quot;product&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;    id&lt;&#x2F;span&gt;&lt;span&gt;: Mapped[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mapped_column(primary_key&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B58900;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    name: Mapped[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mapped_column(String(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt;64&lt;&#x2F;span&gt;&lt;span&gt;), nullable&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B58900;&quot;&gt;False&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt; ProductSubscription&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6C71C4;&quot;&gt;Base&lt;&#x2F;span&gt;&lt;span&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    __tablename__&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; &amp;quot;product_subscription&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;    id&lt;&#x2F;span&gt;&lt;span&gt;: Mapped[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mapped_column(primary_key&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B58900;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    company_id: Mapped[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mapped_column(ForeignKey(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;company.id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    product_id: Mapped[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mapped_column(ForeignKey(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;product.id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt; ProductConfiguration&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6C71C4;&quot;&gt;Base&lt;&#x2F;span&gt;&lt;span&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    __tablename__&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; &amp;quot;product_configuration&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;    id&lt;&#x2F;span&gt;&lt;span&gt;: Mapped[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mapped_column(primary_key&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B58900;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    some_setting: Mapped[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mapped_column(String(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt;64&lt;&#x2F;span&gt;&lt;span&gt;), nullable&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B58900;&quot;&gt;False&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    user_id: Mapped[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mapped_column(ForeignKey(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;user.id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    product_id: Mapped[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mapped_column(ForeignKey(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;product.id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;figure&gt;




    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;five-models-with-relationships.a2c7df6423e8b584.png&quot; alt=&quot;Diagram of the five models described on this blog post: Company, User, Product, ProductSubscription and ProductConfiguration. It uses arrows between the models to indicate the foreign key relationships between them.&quot;&gt;


&lt;figcaption&gt;How the five models relate to each other with the foreign keys. Diagram generated with &lt;a href=&quot;https:&#x2F;&#x2F;plantuml.com&quot;&gt;PlantUML&lt;&#x2F;a&gt;.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Now, I want a relationship on &lt;code&gt;ProductConfiguration&lt;&#x2F;code&gt; that returns the &lt;code&gt;ProductSubscription&lt;&#x2F;code&gt; related to that configuration. In other words, it has to return the &lt;code&gt;ProductSubscription&lt;&#x2F;code&gt; with a &lt;code&gt;ProductSubscription.product_id&lt;&#x2F;code&gt; matching &lt;code&gt;ProductConfiguration.product_id&lt;&#x2F;code&gt;, and with a &lt;code&gt;ProductSubscription.customer_id&lt;&#x2F;code&gt; matching the &lt;code&gt;User.company_id&lt;&#x2F;code&gt; of the &lt;code&gt;User&lt;&#x2F;code&gt; with &lt;code&gt;User.id&lt;&#x2F;code&gt; matching the &lt;code&gt;ProductConfiguration.user_id&lt;&#x2F;code&gt;. Easy-peasy, right?&lt;&#x2F;p&gt;
&lt;p&gt;Well, this is what it looks like as a SQLAlchemy relationship:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt; ProductConfiguration&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6C71C4;&quot;&gt;Base&lt;&#x2F;span&gt;&lt;span&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    __tablename__&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; &amp;quot;product_configuration&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;    id&lt;&#x2F;span&gt;&lt;span&gt;: Mapped[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mapped_column(primary_key&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B58900;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    some_setting: Mapped[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mapped_column(String(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt;64&lt;&#x2F;span&gt;&lt;span&gt;), nullable&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B58900;&quot;&gt;False&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    user_id: Mapped[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mapped_column(ForeignKey(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;user.id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    product_id: Mapped[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mapped_column(ForeignKey(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;product.id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    subscription&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; relationship(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ProductSubscription,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        secondary&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;join(User, ProductSubscription, User.company_id == ProductSubscription.company_id).join(Product, Product.id == ProductSubscription.product_id)&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        primaryjoin&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;and_(ProductConfiguration.product_id == Product.id, ProductConfiguration.user_id == User.id)&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        secondaryjoin&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;and_(ProductSubscription.company_id == User.company_id, ProductSubscription.product_id  == Product.id)&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        uselist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B58900;&quot;&gt;False&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        viewonly&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B58900;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you get confused with this example, try to understand the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.sqlalchemy.org&#x2F;en&#x2F;20&#x2F;orm&#x2F;join_conditions.html#composite-secondary-joins&quot;&gt;example from the SQLAlchemy documentation&lt;&#x2F;a&gt; first, as it combines only four tables instead of five, and it&#x27;s a bit easier to follow.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s also interesting to read about &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.sqlalchemy.org&#x2F;en&#x2F;20&#x2F;orm&#x2F;relationship_api.html#sqlalchemy.orm.relationship.params.secondary&quot;&gt;&lt;code&gt;secondary&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.sqlalchemy.org&#x2F;en&#x2F;20&#x2F;orm&#x2F;relationship_api.html#sqlalchemy.orm.relationship.params.primaryjoin&quot;&gt;&lt;code&gt;primaryjoin&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.sqlalchemy.org&#x2F;en&#x2F;20&#x2F;orm&#x2F;relationship_api.html#sqlalchemy.orm.relationship.params.secondaryjoin&quot;&gt;&lt;code&gt;secondaryjoin&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, I don&#x27;t need to forge this type of relationships often!&lt;&#x2F;p&gt;
&lt;p&gt;Happy coding!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Fix for Rust on Windows not able to find MySQL library</title>
        <published>2024-07-07T00:00:00+00:00</published>
        <updated>2024-07-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/rust-windows-cannot-find-mysql/"/>
        <id>https://sergiswriting.com/rust-windows-cannot-find-mysql/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/rust-windows-cannot-find-mysql/">&lt;p&gt;I was tinkering with the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;diesel.rs&#x2F;&quot;&gt;Diesel ORM&lt;&#x2F;a&gt; on a Rust project, and when building it I faced the following error:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; cargo run&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;Compiling&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; tokio v1.34.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;Compiling&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; tinyvec_macros v0.1.1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;Compiling&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; serde v1.0.193&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;Compiling&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; tinyvec v1.6.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;Compiling&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; unicode-bidi v0.3.13&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;Compiling&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; pin-project v1.1.3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;Compiling&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; axum-core v0.3.4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;Compiling&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; mysqlclient-sys v0.2.5&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;Compiling&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; byteorder v1.5.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;error:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; could not find native static library `&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;mysqlclient&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; perhaps an&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt; -L&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; flag is missing?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;error:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; could not compile `&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;mysqlclient-sys&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;lib&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt; due&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; to previous error&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;warning:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; build failed, waiting for other jobs to finish...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;After looking online, I found this &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;diesel-rs&#x2F;diesel&#x2F;issues&#x2F;1286#issuecomment-369916468&quot;&gt;GitHub comment&lt;&#x2F;a&gt; instructing that I needed to specify the location of &lt;code&gt;mysqlclient.lib&lt;&#x2F;code&gt; using environment variables. Some old comments on that discussion say to point the environment variable to the C connector, but it seems that MySQL moved &lt;code&gt;mysqlclient.lib&lt;&#x2F;code&gt; from the C connector to the Server between versions 6 and 7. So, in my case, I had to set the environment variable to &lt;code&gt;$env:MYSQLCLIENT_LIB_DIR=&#x27;C:\Program Files\MySQL\MySQL Server 8.0\lib&#x27;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;After that, success!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Auto-incrementing IDs on MongoDB</title>
        <published>2024-05-26T00:00:00+00:00</published>
        <updated>2024-05-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/auto-incrementing-ids-mongodb/"/>
        <id>https://sergiswriting.com/auto-incrementing-ids-mongodb/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/auto-incrementing-ids-mongodb/">&lt;p&gt;In relational databases, it is a common pattern to use auto-incremental integers as IDs for table rows. On MongoDB, if you don&#x27;t set an &lt;code&gt;_id&lt;&#x2F;code&gt; field on document creation, the document will get an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.mongodb.com&#x2F;docs&#x2F;manual&#x2F;reference&#x2F;method&#x2F;ObjectId&#x2F;&quot;&gt;ObjectId&lt;&#x2F;a&gt; value automatically assigned to it as &lt;code&gt;_id&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This is fine in most cases because usually, you don&#x27;t need to expose that ID to the users of the API (or on the UI presenting that data). But sometimes you might to create an ID that you want to share with the users, because they might need to reference a specific data object during conversations, and ObjectID values are not suited to that. For example, it&#x27;s easier to say &quot;Hey, invoice INV-204 is still due&quot; than &quot;Hey, invoice 507f1f77bcf86cd799439011 is still due&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;One way to accomplish that is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.mongodb.com&#x2F;resources&#x2F;products&#x2F;platform&#x2F;mongodb-auto-increment&quot;&gt;using triggers&lt;&#x2F;a&gt;, but that&#x27;s a feature only available on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.mongodb.com&#x2F;resources&#x2F;products&#x2F;capabilities&#x2F;database-triggers&quot;&gt;MongoDB Atlas&lt;&#x2F;a&gt; and not for the self-hosted product.&lt;&#x2F;p&gt;
&lt;p&gt;The alternative is to use a collection as a counter for sequences. I found the concept explained on an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.mongodb.com&#x2F;docs&#x2F;v2.6&#x2F;tutorial&#x2F;create-an-auto-incrementing-field&#x2F;&quot;&gt;old version of the official documentation&lt;&#x2F;a&gt;, but I couldn&#x27;t find it on the newest version. Anyway, the idea is to have a collection that will be dedicated to keep track of auto-incrementing IDs. For each collection where you want to use an ID of this kind, you will have a document on this collection.&lt;&#x2F;p&gt;
&lt;p&gt;What makes it work is to make sure that we are generating the new value and updating the collection storing it atomically. This is, that they happen on the same step. We want to avoid this scenario:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Assume our current stored latest ID is 41.&lt;&#x2F;li&gt;
&lt;li&gt;Thread A tries to generate a new ID. It checks the latest stored ID, and it increments it by 1 to get 42.&lt;&#x2F;li&gt;
&lt;li&gt;Thread B tries to generate a new ID. It also checks the latest stored ID, which is still 41, so it decides the next one is 42.&lt;&#x2F;li&gt;
&lt;li&gt;Thread A stores 42 as the latest ID.&lt;&#x2F;li&gt;
&lt;li&gt;Thread B stores 42, again, as latest ID. Problem #1: we didn&#x27;t auto-increment properly.&lt;&#x2F;li&gt;
&lt;li&gt;Thread A stores a document (using the same example as before, an invoice) using that ID, so invoice number 42.&lt;&#x2F;li&gt;
&lt;li&gt;Thread B stores also tries to store a new invoice, using the same ID. This is going to create problem #2, a clash of IDs.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Instead, we want:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Assume our current stored latest ID is 41.&lt;&#x2F;li&gt;
&lt;li&gt;Thread A tries to generate a new ID. It checks the latest stored ID, it increments it by 1 to get 42 and it stores that new value in the database. All these 3 steps (fetch, increment, and store) happen as a single operation.&lt;&#x2F;li&gt;
&lt;li&gt;Thread B tries to generate a new ID. It also checks the latest stored ID, which is now 42, so it decides the next one is 43. Like before, it will fetch, increment, and store the new value in a single operation.&lt;&#x2F;li&gt;
&lt;li&gt;Thread A stores a document using that ID, so invoice number 42.&lt;&#x2F;li&gt;
&lt;li&gt;Thread B stores also tries to store a new invoice, which will be invoice 43.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The following is how to implement this solution with Python using &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;mongoengine.org&#x2F;&quot;&gt;MongoEngine&lt;&#x2F;a&gt;. Specifically, to create invoice IDs as &lt;code&gt;INV-{invoice_number}&lt;&#x2F;code&gt;. The solution can easily be ported to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pymongo.readthedocs.io&#x2F;en&#x2F;stable&#x2F;&quot;&gt;PyMongo&lt;&#x2F;a&gt;, but I like to use MongoEngine to help with data validation and treat data as objects and not as dictionaries.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; mongoengine&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; Document, fields&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;font-style: italic;&quot;&gt;# This is the collection that will store sequences and their values&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt; Sequence&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6C71C4;&quot;&gt;Document&lt;&#x2F;span&gt;&lt;span&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;    id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; fields.StringField(primary_key&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B58900;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    value&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; fields.IntField(required&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B58900;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;, default&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;font-style: italic;&quot;&gt;# This function is used to generate and store a new value for a given sequence&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt; get_next_id&lt;&#x2F;span&gt;&lt;span&gt;(sequence_name:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; str&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; int&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    sequence&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; Sequence.objects(id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;sequence_name).modify(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        upsert&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B58900;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;, new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B58900;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;, inc__value&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; sequence.value&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;font-style: italic;&quot;&gt;# Function to generate invoice IDs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt; generate_invoice_id&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; str&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    invoice_number&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; get_next_id(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;invoice&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt; f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;INV-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;invoice_number&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;font-style: italic;&quot;&gt;# Our example Invoice class leveraging the autogenerated invoice ID&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #586E75;font-weight: bold;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CB4B16;&quot;&gt; Invoice&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6C71C4;&quot;&gt;Document&lt;&#x2F;span&gt;&lt;span&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;    id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; fields.StringField(primary_key&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B58900;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;, default&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;generate_invoice_id)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    description&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; fields.StringField(required&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B58900;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    dollar_amount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; fields.Decimal128Field(required&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B58900;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;font-style: italic;&quot;&gt;# Sample code to create and save a new invoice    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;new_invoice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; Invoice(description&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;Writing a blog post&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, dollar_amount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt;1000&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;new_invoice.save()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Happy coding!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Enabling OpenSSL for Rust in Windows</title>
        <published>2024-05-08T00:00:00+00:00</published>
        <updated>2024-05-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/rust-openssl-windows/"/>
        <id>https://sergiswriting.com/rust-openssl-windows/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/rust-openssl-windows/">&lt;p&gt;The other day I was compiling a Rust project on a Windows 11 environment, and when executing &lt;code&gt;cargo run&lt;&#x2F;code&gt; and I got the following error:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;error:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; failed to run custom build command for openssl-sys v0.9.93&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;note:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; vcpkg did not find openssl: Could not find Vcpkg tree: No vcpkg installation found. Set the VCPKG_ROOT environment variable or run &amp;#39;vcpkg integrate install&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;According to the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;openssl&#x2F;latest&#x2F;openssl&#x2F;&quot;&gt;openssl crate documentation&lt;&#x2F;a&gt;, you can locate the OpenSSL libraries using 3 ways: vendored, automatic or manual.&lt;&#x2F;p&gt;
&lt;p&gt;I didn&#x27;t want to use the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;openssl&#x2F;latest&#x2F;openssl&#x2F;#vendored&quot;&gt;vendored&lt;&#x2F;a&gt; option because I didn&#x27;t want to modify that project&#x27;s &lt;code&gt;Cargo.toml&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;First I tried the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;openssl&#x2F;latest&#x2F;openssl&#x2F;#automatic&quot;&gt;automatic&lt;&#x2F;a&gt; way, which consisted on:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;vcpkg&#x2F;get_started&#x2F;overview&quot;&gt;Installing &lt;code&gt;vcpkg&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Executing &lt;code&gt;.&#x2F;vcpkg&#x2F;vcpkg.exe install openssl&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;But that was not enough. It was still failing to locate the OpenSSL library. So I ended following the instructions for the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;openssl&#x2F;latest&#x2F;openssl&#x2F;#manual&quot;&gt;manual&lt;&#x2F;a&gt; approach. This approach required me to not only perform the two steps mentioned above of the automatic approach, but also set the following environment variables:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;powershell&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;env:OPENSSL_LIB_DIR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;C:\Users\sergi\vcpkg\packages\openssl_x64-windows\lib&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;env:OPENSSL_INCLUDE_DIR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;C:\Users\sergi\vcpkg\packages\openssl_x64-windows\include&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #268BD2;&quot;&gt;env:OPENSSL_DIR&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;quot;C:\Users\sergi\vcpkg\packages\openssl_x64-windows&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;After that, cargo was happy and everything worked as expected!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>How to rotate Gunicorn log files</title>
        <published>2024-04-12T00:00:00+00:00</published>
        <updated>2024-04-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/rotate-gunicorn-logs/"/>
        <id>https://sergiswriting.com/rotate-gunicorn-logs/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/rotate-gunicorn-logs/">&lt;p&gt;A few weeks ago I found myself scratching my head about missing records on the log files of a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;flask.palletsprojects.com&#x2F;en&#x2F;3.0.x&#x2F;&quot;&gt;Flask&lt;&#x2F;a&gt; application running with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gunicorn.org&#x2F;&quot;&gt;Gunicorn&lt;&#x2F;a&gt;. I knew that the requests had been performed and should have been logged, but they were nowhere to be found.&lt;&#x2F;p&gt;
&lt;p&gt;While investigating, I noticed that the rotated log files had way less records than what I expected. Something was going on. After some online surfing, I found out the following crucial pieces of information:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Python&#x27;s standard library logging is thread safe but not process safe.&lt;&#x2F;li&gt;
&lt;li&gt;Gunicorn logs are written by the worker processes, not by the master process.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I figured out that because I was using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;logging.handlers.html#timedrotatingfilehandler&quot;&gt;&lt;code&gt;TimedRotatingFileHandler&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; to rotate the log files daily, I was running into a situation where each worker was not only trying to write to the same file, but each worker also tried to handle the file rotation. I&#x27;m not sure how all these pieces interacted (when was data flushed to disk, if a worker was overwriting other workers&#x27; output, etc.), but definitely there was a conflict that was resulting on data being lost.&lt;&#x2F;p&gt;
&lt;p&gt;And I was not alone on being confused about rotating Gunicorn logs. A sample:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;benoitc&#x2F;gunicorn&#x2F;issues&#x2F;1272&quot;&gt;Gunicorn GitHub issue: [Design Question] How do gunicorn workers log correctly?&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;benoitc&#x2F;gunicorn&#x2F;issues&#x2F;1941&quot;&gt;Gunicorn GitHub issue: (forum) logging from each worker&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Delgan&#x2F;loguru&#x2F;issues&#x2F;365&quot;&gt;Loguru GitHub issue: Issues with rotate&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;encode&#x2F;uvicorn&#x2F;issues&#x2F;896&quot;&gt;Uvicorn GitHub issue: Gunicorn+UvicornWorker,access log not working after logrotate access log file.&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So, I ended taking a pragmatic approach to see by myself how different configurations would work out. I created an environment where I had Gunicorn running a Flask application with 10 Sync workers. I used &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;k6.io&#x2F;&quot;&gt;k6&lt;&#x2F;a&gt; to generate 1000 requests per minute during 3 minutes, which should generate 3000 log records. I recreated this setup with 3 different configurations, but always with a rotation set to 1 minute (or to 500KBs per file, which seemed to be the amount of records generated per minute):&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Python&#x2F;Gunicorn handling the rotation with &lt;code&gt;TimedRotatingFileHandler&lt;&#x2F;code&gt;: at the end of the test I had less than 200 log records among the 3 rotated files. Lots of records lost!&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Python&#x2F;Gunicorn writing to a single file with the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;logging.handlers.html#filehandler&quot;&gt;default &lt;code&gt;FileHandler&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;logrotate&#x2F;logrotate&quot;&gt;logrotate&lt;&#x2F;a&gt; handling the rotation: all the log records kept being written to the first file even after being rotated. So I ended with the most recent non-rotated file with 0 entries and a rotated file with 3000 records.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Python&#x2F;Gunicorn writing to a single file with the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;logging.handlers.html#watchedfilehandler&quot;&gt;&lt;code&gt;WatchedFileHandler&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, and logrotate handling the rotation: the log records were distributed evenly among the different rotated log files, for a total of 3000 records! Success!&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;If you found yourself with the same issue, this is the winning configuration:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;gunicorn_logging.conf&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;font-style: italic;&quot;&gt;; ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[handler_json_log_file]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;=logging.handlers.WatchedFileHandler&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;formatter&lt;&#x2F;span&gt;&lt;span&gt;=json&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;font-style: italic;&quot;&gt;; filename, mode=&amp;#39;a&amp;#39;, encoding=None, delay=True, errors=None&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;args&lt;&#x2F;span&gt;&lt;span&gt;=(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;#39;&#x2F;path&#x2F;to&#x2F;my&#x2F;log_file.log&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt;&amp;#39;a&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, None, True, None)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;font-style: italic;&quot;&gt;;... &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;logrotate.conf&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&#x2F;path&#x2F;to&#x2F;my&#x2F;log_file.log {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    daily&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    rotate 100&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    create&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    missingok&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    notifempty&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;blockquote&gt;
&lt;p&gt;Happy logging!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Best practice for importing the &lt;code&gt;datetime&lt;&#x2F;code&gt; module in Python</title>
        <published>2024-02-04T00:00:00+00:00</published>
        <updated>2024-02-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/import-datetime/"/>
        <id>https://sergiswriting.com/import-datetime/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/import-datetime/">&lt;p&gt;The &lt;code&gt;datetime&lt;&#x2F;code&gt; module is one of the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;15707532&#x2F;import-datetime-v-s-from-datetime-import-datetime&quot;&gt;most controversial&lt;&#x2F;a&gt; Python imports. That both the module and the class share the same name is unfortunate. I&#x27;ve seen many bugs caused by a developer using one thinking they are accessing the other because they didn&#x27;t check how the import was done, and their IDE did not warn them.&lt;&#x2F;p&gt;
&lt;p&gt;The practice I&#x27;ve been putting in place with great success at the teams I&#x27;ve worked on is to use the following form:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; datetime&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; as&lt;&#x2F;span&gt;&lt;span&gt; dt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #93A1A1;font-style: italic;&quot;&gt;# Examples of usage:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;published_on&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; dt.datetime(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt;2024&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt; 22&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt; 12&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;today&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; dt.datetime.today()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;five_seconds&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; dt.timedelta(seconds&lt;&#x2F;span&gt;&lt;span style=&quot;color: #859900;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D33682;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The benefits of this form are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;It removes the ambiguity of &lt;code&gt;datetime&lt;&#x2F;code&gt; being the module or the class:
&lt;ul&gt;
&lt;li&gt;If you know the convention, you know that &lt;code&gt;dt&lt;&#x2F;code&gt; is the module and &lt;code&gt;dt.datetime&lt;&#x2F;code&gt; is the class.&lt;&#x2F;li&gt;
&lt;li&gt;If you don&#x27;t know the convention, and see code using it, it&#x27;s intuitive to figure out that &lt;code&gt;dt.datetime&lt;&#x2F;code&gt; is the class and not the module. Worst case, you will check the import statement to confirm that, as it&#x27;s a pattern you haven&#x27;t seen before.&lt;&#x2F;li&gt;
&lt;li&gt;If you don&#x27;t know the convention, and want to use &lt;code&gt;datetime&lt;&#x2F;code&gt; on a file where the proposed form is used, you won&#x27;t accidentally just use &lt;code&gt;datetime&lt;&#x2F;code&gt; because it&#x27;s not imported as &lt;code&gt;import datetime&lt;&#x2F;code&gt; or &lt;code&gt;from datetime import datetime&lt;&#x2F;code&gt; (I am assuming here that your IDE would warn you of undefined names&#x2F;variables). That will force you to go to the import section and discover that it&#x27;s using &lt;code&gt;import datetime as dt&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;It frees the namespace to use &lt;code&gt;datetime&lt;&#x2F;code&gt; and &lt;code&gt;date&lt;&#x2F;code&gt; as variables. But be careful with that! Make sure you are using it on a small scope where another developer can see without scrolling that they are defined variables. Otherwise, there is the possibility of somebody not paying attention try to use them as the module or the class.&lt;&#x2F;li&gt;
&lt;li&gt;It&#x27;s more beautiful than &lt;code&gt;datetime.datetime&lt;&#x2F;code&gt; :D&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;One could argue: well, if you are just going to adopt a convention and make sure the whole team uses it, why not stick to &lt;code&gt;import datetime&lt;&#x2F;code&gt; or &lt;code&gt;from datetime import X&lt;&#x2F;code&gt;? If everybody knows, it&#x27;s fine!&lt;&#x2F;p&gt;
&lt;p&gt;The issue with that is existing code. If people have been mixing styles, you might open an old file, try to use &lt;code&gt;datetime&lt;&#x2F;code&gt;, see that is available in that namespace and wonder &quot;Uh... is this already using the new convention&quot;? On the other hand, if you find &lt;code&gt;dt&lt;&#x2F;code&gt; available on the namespace, it is almost guaranteed (unless somebody has been very creative with their imports) that the file is already using the convention.&lt;&#x2F;p&gt;
&lt;p&gt;All that said, nowadays, decent IDEs like PyCharm are able to figure out if you are working with the module or the class, making the whole post a bit of a moot point on some aspects. Still, consistency on coding is key, so if you don&#x27;t adopt this form, at least consider choosing one for everybody on your team to use.&lt;&#x2F;p&gt;
&lt;p&gt;Happy coding!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Tip: Speed up Python dictionary lookups</title>
        <published>2024-01-14T00:00:00+00:00</published>
        <updated>2024-01-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/python-sys-intern/"/>
        <id>https://sergiswriting.com/python-sys-intern/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/python-sys-intern/">&lt;p&gt;Last week at work, there was a conversation about managing a fairly large key-value data structure using Python. A colleague recommended checking &lt;code&gt;sys.intern&lt;&#x2F;code&gt; to improve performance. I can&#x27;t add much beyond what&#x27;s already covered in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3.12&#x2F;library&#x2F;sys.html?highlight=sys.intern#sys.intern&quot;&gt;documentation&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;sys.intern(string)&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Enter &lt;em&gt;string&lt;&#x2F;em&gt; in the table of “interned” strings and return the interned string – which is &lt;em&gt;string&lt;&#x2F;em&gt; itself or a copy. Interning strings is useful to gain a little performance on dictionary lookup – if the keys in a dictionary are interned, and the lookup key is interned, the key comparisons (after hashing) can be done by a pointer compare instead of a string compare. Normally, the names used in Python programs are automatically interned, and the dictionaries used to hold module, class or instance attributes have interned keys.&lt;&#x2F;p&gt;
&lt;p&gt;Interned strings are not immortal; you must keep a reference to the return value of &lt;code&gt;intern()&lt;&#x2F;code&gt; around to benefit from it.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I thought it would be nice to not only share the existence of that, but to provide code examples of how to use it. I concocted these examples, but when I measured the performance of the code using &quot;interned&quot; strings and the code using &quot;regular&quot; strings, the timings were the same. I iterated a few times to see if I was misunderstanding something, but long story short, I gave up and decided to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;77762241&#x2F;how-do-you-use-pythons-string-interning&quot;&gt;post on Stak Overflow&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;a&#x2F;77762410&#x2F;1101750&quot;&gt;The answer to this enigma was humbling&lt;&#x2F;a&gt;. I wasn&#x27;t aware of Python&#x27;s optimizations on string handling. Nick does a good job explaining them, and then offering an actual example where you can see how to use interning effectively.&lt;&#x2F;p&gt;
&lt;p&gt;In conclusion, my advice would be this: whenever you come across claims of a performance boost, verify for yourself that it&#x27;s actually effective in your specific use case!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Better colors for moved text in Git</title>
        <published>2024-01-06T00:00:00+00:00</published>
        <updated>2024-01-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/better-colors-for-moved-text-in-git/"/>
        <id>https://sergiswriting.com/better-colors-for-moved-text-in-git/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/better-colors-for-moved-text-in-git/">&lt;p&gt;If you move a block of text&#x2F;code around, and then you run &lt;code&gt;git diff&lt;&#x2F;code&gt;, you will see that the moved text is displayed like any other change. If you are using the default colors, the old location will show in red and the new one in green. So, we can&#x27;t quickly tell if that change only involves moving that block around, or if in addition to the displacement something has actually changed inside the block.&lt;&#x2F;p&gt;
&lt;p&gt;Well, Git has a setting to change that behaviour: &lt;code&gt;color-moved&lt;&#x2F;code&gt;. According to the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git-scm.com&#x2F;docs&#x2F;diff-options&#x2F;2.19.0#Documentation&#x2F;diff-options.txt---color-movedltmodegt&quot;&gt;documentation&lt;&#x2F;a&gt;, at the date of this article, there are 6 different modes. I recommend trying them out and see which one you like the most. With the couple of examples I tried I couldn&#x27;t appreciate a difference between &lt;code&gt;plain&lt;&#x2F;code&gt; and &lt;code&gt;zebra&lt;&#x2F;code&gt;, so I think I&#x27;ll stick to &lt;code&gt;zebra&lt;&#x2F;code&gt; as I liked it more than the others.&lt;&#x2F;p&gt;
&lt;p&gt;I must say that I got very confused reading that the default behaviour is not the &lt;code&gt;default&lt;&#x2F;code&gt; mode, but the &lt;code&gt;no&lt;&#x2F;code&gt; mode.&lt;&#x2F;p&gt;
&lt;p&gt;If you don&#x27;t know how to set this setting, you have two ways. One is running the command &lt;code&gt;git config --global diff.colormoved &quot;zebra&quot;&lt;&#x2F;code&gt;. The other is manually editing the config file. If you have a &lt;code&gt;.gitconfig&lt;&#x2F;code&gt; file on your home directory or on your repository, add the following lines:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #657B83; background-color: #FDF6E3;&quot;&gt;&lt;code data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[diff]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #859900;&quot;&gt;  colormoved&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #2AA198;&quot;&gt; &amp;quot;zebra&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Another setting related to moving code that is very useful is &lt;code&gt;color-modev-ws&lt;&#x2F;code&gt;. Citing the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git-scm.com&#x2F;docs&#x2F;diff-options&#x2F;2.19.0#Documentation&#x2F;diff-options.txt---color-moved-wsltmodesgt&quot;&gt;documentation&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;This configures how white spaces are ignored when performing the move detection for --color-moved.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;What does that mean? Well, it is not rare that when moving a block of code we change its indentation. Maybe the block was inside a top level function (one indentation level) and now it&#x27;s inside a method inside a class (two indentation levels). If you set this setting to &lt;code&gt;allow-indentation-change&lt;&#x2F;code&gt;, the diff will be more &quot;clever&quot; and figure out that this is still only a block displacement and not a regular text modification. So, it will apply the right colors on the diff according to your &lt;code&gt;color-moved&lt;&#x2F;code&gt; setting.&lt;&#x2F;p&gt;
&lt;p&gt;This post was inspired by matklad&#x27;s post &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matklad.github.io&#x2F;2023&#x2F;12&#x2F;31&#x2F;git-things.html&quot;&gt;&lt;cite&gt;Git Things&lt;&#x2F;cite&gt;&lt;&#x2F;a&gt;, where they briefly mention both settings explained above.&lt;&#x2F;p&gt;
&lt;p&gt;Happy coding!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>How to create a Linux Desktop entry for PyCharm</title>
        <published>2023-12-19T00:00:00+00:00</published>
        <updated>2023-12-19T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/pycharm-shortcut/"/>
        <id>https://sergiswriting.com/pycharm-shortcut/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/pycharm-shortcut/">&lt;p&gt;Unless your Linux distribution offers a package for PyCharm, you have to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.jetbrains.com&#x2F;pycharm&#x2F;download&#x2F;?section=linux&quot;&gt;download a tar.gz from JetBrains&#x27; website&lt;&#x2F;a&gt;, unpack it, and then run the &lt;code&gt;PyCharm.sh&lt;&#x2F;code&gt; script. There is no installer; PyCharm launches directly.&lt;&#x2F;p&gt;
&lt;p&gt;Since you are likely using a Desktop environment (PyCharm being a graphical IDE), you may want want a convenient Desktop launcher to avoid opening a shell just for that. Well, the folks at JetBrains already provide this option, but it is a bit hidden. Start Pycharm, then on the menu bar, go to &lt;code&gt;Tools &amp;gt; Create Desktop Entry...&lt;&#x2F;code&gt;, and that&#x27;s it! This will create the file &lt;code&gt;jetbrains-pycharm.desktop&lt;&#x2F;code&gt; either in &lt;code&gt;~&#x2F;.local&#x2F;share&#x2F;applications&lt;&#x2F;code&gt; or on &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;applications&lt;&#x2F;code&gt; if you selected &quot;Create the entry for all users&quot;. Your Desktop environment should pick it up automatically and display it on you list of applications.&lt;&#x2F;p&gt;
&lt;p&gt;Happy coding!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>How to pair a Bluetooth keyboard in Windows 11</title>
        <published>2023-12-15T00:00:00+00:00</published>
        <updated>2023-12-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/pair-bluetooth-keyboard-windows/"/>
        <id>https://sergiswriting.com/pair-bluetooth-keyboard-windows/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/pair-bluetooth-keyboard-windows/">&lt;p&gt;I am the happy owner of a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20230529000709&#x2F;https:&#x2F;&#x2F;mechanicalkeyboards.com&#x2F;shop&#x2F;index.php?l=product_detail&amp;amp;p=7940&quot;&gt;Leopold FC660MBT Vapor 65% Bluetooth&lt;&#x2F;a&gt;. I decided that this would be my in-office keyboard instead of the cheap one provided at my current company.&lt;&#x2F;p&gt;
&lt;p&gt;My work computer is a ThinkPad laptop running Windows 11, so I was expecting the pairing to be smooth as butter. To my surprise, when I tried to pair it, Windows greeted me with a dialog asking for a PIN. Well, that&#x27;s funny. Asking to enter a PIN to be able to use the device that would allow you to actually type that PIN. Fortunately, it&#x27;s a laptop, and I can use the built-in keyboard instead.&lt;&#x2F;p&gt;
&lt;figure&gt;




    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;add-bluetooth-device-dialog.eedf0a4f934d6256.png&quot; alt=&quot;Windows dialog for adding a new Bluetooth device. It is asking the user to enter a PIN for the device FC660MBT.&quot;&gt;


&lt;figcaption&gt;Asking the user to type a PIN to be able to connect a keyboard. Something about that seems wrong.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;But then, what PIN? Maybe it&#x27;s a default value the manufacturer provides on the manual. Nope, nothing there. Maybe it&#x27;s on the frame or on some sticker? Nada. Confusion.&lt;&#x2F;p&gt;
&lt;p&gt;After searching online, I finally found a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;answers.microsoft.com&#x2F;en-us&#x2F;windows&#x2F;forum&#x2F;all&#x2F;what-is-the-pairing-pin-for-my-microsoft-bluetooth&#x2F;bbfa9907-4854-4a85-ad3b-7f2084a13d4d&quot;&gt;forum post with the solution&lt;&#x2F;a&gt; that worked for me. I summarize it here: &lt;strong&gt;using a working keyboard, type 0000 as the PIN and accept. Then on the new keyboard, type 0000 and press Enter&lt;&#x2F;strong&gt;. So easy but at the same time so arcane.&lt;&#x2F;p&gt;
&lt;p&gt;Many people on that linked post report that it didn&#x27;t work for them. So, if you are reading this article because you are in this situation, I wish you the best of luck!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>This is a blog</title>
        <published>2023-11-15T00:00:00+00:00</published>
        <updated>2023-11-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Sergi Pons Freixes
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://sergiswriting.com/this-is-a-blog/"/>
        <id>https://sergiswriting.com/this-is-a-blog/</id>
        
        <content type="html" xml:base="https://sergiswriting.com/this-is-a-blog/">&lt;p&gt;I used to have a blog. It was a mixture of tech articles — mostly about Linux, Open Source and privacy — and random ramblings. I wrote on different languages, depending on whom I thought could be interested on reading a particular article.&lt;&#x2F;p&gt;
&lt;p&gt;As many blogs, it eventually died of inactivity. I kept it around for many years in case some tech articles were still useful for somebody. At some point I considered that these articles were obsolete, so I got rid of it. I must have a backup somewhere for nostalgic reasons.&lt;&#x2F;p&gt;
&lt;p&gt;During the past few months I&#x27;ve started doing more contributions to open source projects than I did before. I like to think that I&#x27;ve also grown professionally, and there are some thoughts that I&#x27;ve considered sharing as they could be useful to others.&lt;&#x2F;p&gt;
&lt;p&gt;And then, I joined &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;floss.social&#x2F;@sergi&quot;&gt;Mastodon&lt;&#x2F;a&gt;, and found like-minded peers much easier than I did on Twitter — aka X — and re-discovered that &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.theverge.com&#x2F;23513418&#x2F;bring-back-personal-blogging&quot;&gt;personal blogs are way better than social media&lt;&#x2F;a&gt; for some content and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;whereismytribe.net&#x2F;back-to-rss&quot;&gt;RSS is coming back!&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;So, here it is. A blog.&lt;&#x2F;p&gt;
&lt;figure&gt;




    &lt;img class=&quot;[ post-img ]&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;sergiswriting.com&amp;#x2F;processed_images&amp;#x2F;typewriter.2167f664bd958aa9.jpg&quot; alt=&quot;This image shows an old, vintage typewriter placed on a wooden surface. The typewriter is black and has round keys with characters printed on them. The brand “ROYAL” is visible at the top of the typewriter. It’s placed on a surface made of horizontal wooden planks, showcasing a rustic aesthetic. There are visible signs of wear and age on both the typewriter and the wooden surface, giving it an antique look.&quot;&gt;


&lt;figcaption&gt;Photo by &lt;a href=&quot;https:&#x2F;&#x2F;pixnio.com&#x2F;objects&#x2F;typewriter-vintage-keyboard-device-retro-antique-old-nostalgia&quot;&gt;Pixnio&lt;&#x2F;a&gt;&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;</content>
        
    </entry>
</feed>
