<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="https://www.yellowduck.be/pretty-atom-feed-v3.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>🐥 YellowDuck.be</title>
  <id>https://www.yellowduck.be/posts/feed</id>
  <updated>2026-10-24T17:00:00Z</updated>
  <author>
    <name>Pieter Claerhout</name>
    <email>pieter@yellowduck.be</email>
  </author>
  <link rel="self" href="https://www.yellowduck.be/posts/feed"/>
  <link rel="alternate" href="https://www.yellowduck.be"/>
  <entry>
    <id>https://www.yellowduck.be/posts/fixing-header-too-long-when-downloading-precompiled-nifs-in-elixir</id>
    <title>🐥 Fixing &quot;header_too_long&quot; When Downloading Precompiled NIFs in Elixir</title>
    <updated>2026-10-24T17:00:00Z</updated>
    <published>2026-10-24T17:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/fixing-header-too-long-when-downloading-precompiled-nifs-in-elixir"/>
    <content type="html">&lt;p&gt;If you've recently run &lt;code&gt;mix compile&lt;/code&gt; on an Elixir project that uses precompiled NIFs, such as &lt;a href=&quot;https://github.com/leandrocp/mdex&quot;&gt;mdex&lt;/a&gt; or &lt;a href=&quot;https://github.com/leandrocp/lumis&quot;&gt;lumis&lt;/a&gt;, you may have seen an error like this:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-plaintext&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;[info] Attempt 1 failed with {:error, &amp;quot;couldn&amp;#39;t fetch NIF from https://github.com/...: {:error, {:header_too_long, {:max, 10240}}}&amp;quot;}&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;== Compilation error in file lib/mdex_native/native.ex ==&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;** (RuntimeError) Error while downloading precompiled NIF: couldn&amp;#39;t fetch NIF from https://github.com/...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is frustrating because &lt;code&gt;mix deps.get&lt;/code&gt; succeeds. The problem only appears during compilation.&lt;/p&gt;
&lt;h1&gt;What's Going On?&lt;/h1&gt;
&lt;p&gt;Elixir libraries that include native code (Rust, C, etc.) often ship precompiled binaries called NIFs (Native Implemented Functions), so you don't need a native toolchain to use them. The library &lt;a href=&quot;https://github.com/philss/rustler_precompiled&quot;&gt;&lt;code&gt;rustler_precompiled&lt;/code&gt;&lt;/a&gt; handles downloading the right binary for your platform at compile time.&lt;/p&gt;
&lt;p&gt;Under the hood, &lt;code&gt;rustler_precompiled&lt;/code&gt; uses &lt;a href=&quot;https://github.com/elixir-mint/mint&quot;&gt;Mint&lt;/a&gt;, an Elixir HTTP client, to fetch NIF archives from GitHub Releases.&lt;/p&gt;
&lt;p&gt;Here's the problem: GitHub's CDN now returns &lt;strong&gt;very large response headers&lt;/strong&gt; on release asset redirect requests—headers that exceed Mint's default maximum of &lt;strong&gt;10,240 bytes&lt;/strong&gt; (10 KB). When Mint hits that cap, it aborts the request with &lt;code&gt;{:header_too_long, {:max, 10240}}&lt;/code&gt;, and compilation fails.&lt;/p&gt;
&lt;p&gt;This isn't a bug in your code. It's an upstream issue involving GitHub's infrastructure and Mint's conservative defaults.&lt;/p&gt;
&lt;h1&gt;Three Ways to Fix It&lt;/h1&gt;
&lt;h2&gt;Option 1 — Download the NIF Manually with curl (Fastest)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;curl&lt;/code&gt; doesn't have the same header size restriction. You can download the NIF archive directly and let &lt;code&gt;rustler_precompiled&lt;/code&gt; pick it up from its cache on the next compile.&lt;/p&gt;
&lt;p&gt;First, find the download URLs in the error message in your terminal, then fetch the archive:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-bash&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-function-call&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;-L&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;/tmp/your_nif.tar.gz&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&amp;quot;https://github.com/.../your_nif.tar.gz&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;rustler_precompiled&lt;/code&gt; caches NIFs in &lt;code&gt;~/.cache/rustler_precompiled/&lt;/code&gt; (Linux/macOS). Copy the downloaded archive there, then rerun &lt;code&gt;mix compile&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Option 2 — Update the Dependency&lt;/h2&gt;
&lt;p&gt;The maintainers of these libraries are aware of the issue. A newer release may have switched away from Mint for the download step or increased Mint's header limit. Try:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-bash&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-function-call&quot;&gt;mix&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;deps.update&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;your_nif_package&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;span class=&quot;l-function-call&quot;&gt;mix&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;compile&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the cleanest long-term fix if a patched version is available.&lt;/p&gt;
&lt;h2&gt;Option 3 — Force Build from Source&lt;/h2&gt;
&lt;p&gt;If you have the &lt;a href=&quot;https://rustup.rs/&quot;&gt;Rust toolchain&lt;/a&gt; installed, you can tell &lt;code&gt;rustler_precompiled&lt;/code&gt; to compile the NIF locally instead of downloading it.&lt;/p&gt;
&lt;p&gt;Add this to &lt;code&gt;config/dev.exs&lt;/code&gt; (or &lt;code&gt;config/config.exs&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-function-call&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:rustler_precompiled&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:force_build&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;your_nif_package: &lt;/span&gt;&lt;span class=&quot;l-boolean&quot;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And add Rustler as an optional dependency in &lt;code&gt;mix.exs&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:rustler&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&amp;quot;&amp;gt;= 0.0.0&amp;quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;optional: &lt;/span&gt;&lt;span class=&quot;l-boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run &lt;code&gt;mix deps.get &amp;amp;&amp;amp; mix compile&lt;/code&gt;. Compilation will take longer because it builds native code, but it will work regardless of GitHub's header behavior.&lt;/p&gt;
&lt;h1&gt;Which Should You Choose?&lt;/h1&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Recommended fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Just need to unblock right now&lt;/td&gt;
&lt;td&gt;Option 1 (manual curl)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A patched release is available&lt;/td&gt;
&lt;td&gt;Option 2 (update deps)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You control the environment and have Rust&lt;/td&gt;
&lt;td&gt;Option 3 (force build)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI/CD pipeline is broken&lt;/td&gt;
&lt;td&gt;Option 1 or 3, depending on whether Rust is available&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1&gt;The Broader Lesson&lt;/h1&gt;
&lt;p&gt;This failure mode—a third-party infrastructure change silently breaking your build—is a good reminder to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pin your NIF dependency versions so you're not surprised by new behavior after an update.&lt;/li&gt;
&lt;li&gt;Cache precompiled NIFs in CI so you're not downloading them on every build.&lt;/li&gt;
&lt;li&gt;Have a fallback strategy (force build) for environments where downloading isn't reliable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Precompiled NIFs are a major convenience for Elixir developers who don't want to manage native toolchains. But they introduce a network dependency at compile time, and as this issue shows, that dependency can break in unexpected ways.&lt;/p&gt;
&lt;h1&gt;The Proper Fix&lt;/h1&gt;
&lt;p&gt;The poeple behind &lt;a href=&quot;https://github.com/philss/rustler_precompiled&quot;&gt;&lt;code&gt;rustler_precompiled&lt;/code&gt;&lt;/a&gt; are working on a proper fix. You can follow the progress with &lt;a href=&quot;https://github.com/philss/rustler_precompiled/pull/113&quot;&gt;this pull request&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/http&quot;&gt;#http&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/elixir&quot;&gt;#elixir&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/terminal&quot;&gt;#terminal&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/my-code-used-10gb-ram-now-it-uses-300mb</id>
    <title>🔗 My code used 10GB RAM. Now it uses 300MB.</title>
    <updated>2026-10-24T13:00:00Z</updated>
    <published>2026-10-24T13:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/my-code-used-10gb-ram-now-it-uses-300mb"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Years of writing inefficient Python code taught a valuable lesson: better hardware isn't always the answer. In a recent project involving document ingestion from PDFs, a developer experienced 10GB of RAM usage, leading to slow performance and frustrating delays. By implementing three key tricks—avoiding memory-heavy libraries like Pandas, leveraging generators for efficient data handling, and enforcing garbage collection—the same process now operates at only 300MB RAM usage. These changes not only streamlined execution but also significantly reduced runtime, from over 94 minutes to just 41. The article serves as a reminder that careful optimization can transform performance without reliance on advanced tools. Simple techniques often lead to impactful improvements, proving that sometimes the best solutions are hiding in plain sight.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://freedium-mirror.cfd/https%3A%2F%2Fpython.plainenglish.io%2Fmy-code-used-10gb-ram-now-it-uses-300mb-d15a559d229a&quot;&gt;Continue reading on &lt;strong&gt;freedium-mirror.cfd&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/pdf&quot;&gt;#pdf&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/python&quot;&gt;#python&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/nabla-the-isometric-color-font-by-arthur-reinders-folmer-just-van-rossum</id>
    <title>🔗 Nabla — the isometric color font by Arthur Reinders Folmer &amp; Just van Rossum</title>
    <updated>2026-10-24T08:00:00Z</updated>
    <published>2026-10-24T08:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/nabla-the-isometric-color-font-by-arthur-reinders-folmer-just-van-rossum"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Nabla is setting a new standard in typography with its innovative use of the COLRv1 color font format. Designed by Arthur Reinders Folmer and engineered by Just van Rossum, it brings vibrant, customizable type to life, drawing inspiration from vintage computer games. This open-source font allows users to experiment with isometric letter shapes, gradients, and depth variations, making it a unique tool for creating eye-catching designs. Although it currently works best in Chrome, support for other browsers is on the horizon. Ideal for designers looking to push boundaries in color and form, Nabla promises a vibrant typographic future.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://nabla.typearture.com/&quot;&gt;Continue reading on &lt;strong&gt;nabla.typearture.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/frontend&quot;&gt;#frontend&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/css&quot;&gt;#css&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/tools&quot;&gt;#tools&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/streaming-zip-downloads-in-phoenix-with-zstream</id>
    <title>🐥 Streaming ZIP downloads in Phoenix with zstream</title>
    <updated>2026-10-23T17:00:00Z</updated>
    <published>2026-10-23T17:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/streaming-zip-downloads-in-phoenix-with-zstream"/>
    <content type="html">&lt;p&gt;When you need to let users download multiple files as a ZIP in a Phoenix application, creating the archive upfront is often wasteful. It costs disk space, adds latency, and does not scale well for large files.&lt;/p&gt;
&lt;p&gt;A better approach is to &lt;strong&gt;stream the ZIP archive directly to the browser&lt;/strong&gt; while it's being generated. In Elixir, the &lt;code&gt;zstream&lt;/code&gt; library makes this straightforward and production-ready.&lt;/p&gt;
&lt;p&gt;This post shows how to stream a ZIP file from a Phoenix controller without creating temporary files.&lt;/p&gt;
&lt;h1&gt;The idea&lt;/h1&gt;
&lt;p&gt;The solution combines three pieces:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Plug.Conn.send_chunked/2&lt;/code&gt; to start a streaming HTTP response&lt;/li&gt;
&lt;li&gt;&lt;code&gt;zstream&lt;/code&gt; to generate ZIP data incrementally&lt;/li&gt;
&lt;li&gt;File streams (&lt;code&gt;File.stream!/3&lt;/code&gt;) to avoid loading files into memory&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The browser starts downloading immediately, while the server produces ZIP chunks on the fly.&lt;/p&gt;
&lt;h1&gt;Adding the dependency&lt;/h1&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword-function&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;deps&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;    &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:zstream&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&amp;quot;~&amp;gt; 0.6&amp;quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;  &lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Streaming a ZIP from a controller&lt;/h1&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword-function&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;MyAppWeb.DownloadController&lt;/span&gt; &lt;span class=&quot;l-keyword-function&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;MyAppWeb&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:controller&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;  &lt;span class=&quot;l-keyword-function&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;images&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-comment&quot;&gt;_params&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;files&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;      &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&amp;quot;image1.jpg&amp;quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&amp;quot;/data/images/image1.jpg&amp;quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;      &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&amp;quot;image2.png&amp;quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&amp;quot;/data/images/image2.png&amp;quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;    &lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;10&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;conn&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;11&quot;&gt;      &lt;span class=&quot;l-variable&quot;&gt;conn&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;12&quot;&gt;      &lt;span class=&quot;l-operator&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;l-function-call&quot;&gt;put_resp_header&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&amp;quot;content-type&amp;quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&amp;quot;application/zip&amp;quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;13&quot;&gt;      &lt;span class=&quot;l-operator&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;l-function-call&quot;&gt;put_resp_header&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;14&quot;&gt;        &lt;span class=&quot;l-string&quot;&gt;&amp;quot;content-disposition&amp;quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;15&quot;&gt;        &lt;span class=&quot;l-string&quot;&gt;~s(&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;attachment; filename=&amp;quot;images.zip&amp;quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;16&quot;&gt;      &lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;17&quot;&gt;      &lt;span class=&quot;l-operator&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;l-function-call&quot;&gt;send_chunked&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;18&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;19&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;zip_stream&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;20&quot;&gt;      &lt;span class=&quot;l-module&quot;&gt;Zstream&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;zip&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;21&quot;&gt;        &lt;span class=&quot;l-module&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;22&quot;&gt;          &lt;span class=&quot;l-module&quot;&gt;Zstream&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;stream!&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-number&quot;&gt;64_000&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;23&quot;&gt;        &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;24&quot;&gt;      &lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;25&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;26&quot;&gt;    &lt;span class=&quot;l-module&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;reduce_while&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;zip_stream&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;conn&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;27&quot;&gt;      &lt;span class=&quot;l-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Plug.Conn&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;28&quot;&gt;        &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:cont&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;29&quot;&gt;        &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:error&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:closed&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:halt&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;30&quot;&gt;      &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;31&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;32&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;33&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What's happening here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each file is represented as a &lt;code&gt;Zstream&lt;/code&gt; entry&lt;/li&gt;
&lt;li&gt;The file contents are streamed from disk&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Zstream.zip/1&lt;/code&gt; produces a stream of ZIP binary chunks&lt;/li&gt;
&lt;li&gt;Each chunk is forwarded to the client using &lt;code&gt;Plug.Conn.chunk/2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At no point is the full ZIP or any full file loaded into memory.&lt;/p&gt;
&lt;h1&gt;Adding folders to the ZIP&lt;/h1&gt;
&lt;p&gt;ZIP archives don't require explicit directory entries. Just use paths in the entry name:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-module&quot;&gt;Zstream&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&amp;quot;images/image1.jpg&amp;quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;stream!&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-number&quot;&gt;64_000&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Why this approach works well&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;No temporary ZIP files&lt;/li&gt;
&lt;li&gt;Constant memory usage&lt;/li&gt;
&lt;li&gt;Handles large files gracefully&lt;/li&gt;
&lt;li&gt;The download starts immediately in the browser&lt;/li&gt;
&lt;li&gt;Works with standard reverse proxies like Nginx or Caddy&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Common pitfalls&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Always call &lt;code&gt;send_chunked/2&lt;/code&gt; before streaming data&lt;/li&gt;
&lt;li&gt;Handle &lt;code&gt;{:error, :closed}&lt;/code&gt; when the client disconnects&lt;/li&gt;
&lt;li&gt;Use reasonably large chunk sizes (32–128 KB)&lt;/li&gt;
&lt;li&gt;Ensure file paths and names are trusted to avoid ZIP path traversal&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Using this from LiveView&lt;/h1&gt;
&lt;p&gt;LiveView cannot stream responses itself. The usual pattern is to trigger a navigation to the controller action:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-function-call&quot;&gt;push_navigate&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;to: &lt;/span&gt;&lt;span class=&quot;l-string-special&quot;&gt;~p&amp;quot;&lt;/span&gt;/downloads/images&lt;span class=&quot;l-string-special&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The controller then handles the streaming ZIP download as shown above.&lt;/p&gt;
&lt;h1&gt;Closing thoughts&lt;/h1&gt;
&lt;p&gt;If you need to package many files into a ZIP download in Phoenix, &lt;code&gt;zstream&lt;/code&gt; is a simple and robust solution. It fits naturally with Plug's chunked responses and keeps resource usage predictable, even under load.&lt;/p&gt;
&lt;p&gt;For most applications, this is the cleanest way to offer ZIP downloads without touching the filesystem at all.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/pattern&quot;&gt;#pattern&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/http&quot;&gt;#http&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/elixir&quot;&gt;#elixir&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/phoenix&quot;&gt;#phoenix&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/rss-feed-best-practices</id>
    <title>🔗 RSS feed best practices</title>
    <updated>2026-10-23T13:00:00Z</updated>
    <published>2026-10-23T13:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/rss-feed-best-practices"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Are you leveraging RSS feeds effectively? Kevin Cox outlines essential tips for optimizing your RSS and Atom feeds for better reach and performance. He emphasizes the importance of using standardized formats like RSS 2 and Atom, ensuring all URLs are absolute, and setting proper content types. He also covers best practices for discovery, content presentation, and maintaining clear entry IDs, which help avoid duplicates. The article addresses security with a strong recommendation for HTTPS, along with a detailed discussion on providing full content versus summaries. Ultimately, these guidelines can help ensure that your feeds are user-friendly and accessible, improving your blog's visibility in a crowded space.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://kevincox.ca/2022/05/06/rss-feed-best-practices/&quot;&gt;Continue reading on &lt;strong&gt;kevincox.ca&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/http&quot;&gt;#http&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/introducing-citry</id>
    <title>🔗 Introducing Citry</title>
    <updated>2026-10-23T08:00:00Z</updated>
    <published>2026-10-23T08:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/introducing-citry"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Why build a fully typed frontend framework for Python? Juro Oravec shares his journey in creating Citry, inspired by React and Vue, which promises to unify frontend development in Python. After years of grappling with the limitations of Django templates, he developed Citry to ensure clearer input handling, better error management, and a cohesive component structure. With Citry, developers can write components as Python classes that include HTML, JS, and CSS all together. This results in a much smoother development experience, especially when integrated with existing Python web frameworks like Django and FastAPI. Citry's public beta even offers a playground for instant experimentation. By introducing error-checking directly into the development process, Citry could reshape how developers approach frontend tasks with Python, making it an intriguing choice for modern web applications.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://citry.dev/blog/introducing-citry/&quot;&gt;Continue reading on &lt;strong&gt;citry.dev&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/frontend&quot;&gt;#frontend&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/python&quot;&gt;#python&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/django&quot;&gt;#django&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/how-to-build-innovation-teams</id>
    <title>🔗 How to build innovation teams</title>
    <updated>2026-10-22T17:00:00Z</updated>
    <published>2026-10-22T17:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/how-to-build-innovation-teams"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Why do people believe you can build an innovation team by hiring a few smart people and giving them a creative space? Phil McKinney's article challenges this notion, sharing insights from his experience leading innovation teams at HP and CableLabs. He argues that without a strong foundational culture and the right roles, innovation efforts often fail. McKinney outlines six essential steps for building effective innovation teams, emphasizing the importance of recruiting specific roles rather than just talent. He illustrates how processes must be tailored to a company’s unique context rather than blindly adopted from successful examples elsewhere. The piece is not just theoretical; it’s based on practical lessons learned from McKinney’s experiences, including a cautionary tale about the pitfalls of imitating others without adaptation. Readers seeking to cultivate innovation will find valuable strategies and insights here.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://www.philmckinney.com/how-to-build-innovation-teams-my-playbook-from-hp-and-cablelabs/&quot;&gt;Continue reading on &lt;strong&gt;www.philmckinney.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/seven-fujifilm-settings-that-ruin-your-photos</id>
    <title>🔗 Seven Fujifilm settings that ruin your photos</title>
    <updated>2026-10-22T13:00:00Z</updated>
    <published>2026-10-22T13:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/seven-fujifilm-settings-that-ruin-your-photos"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Fujifilm cameras often come with default settings that can hinder photo quality, like Auto ISO and electronic shutter defaults, leading to issues such as blurry low-light images and incorrect white balance. Chris Lee from pal2tech points out seven common mistakes made by Fujifilm users, including the problem of custom settings potentially overwriting themselves and SD card configuration errors that prevent real-time backups. He emphasizes the importance of revisiting and adjusting factory settings to meet specific photography needs, suggesting that these tailored tweaks can significantly improve shooting outcomes. Lee provides insights on how these settings operate within the Fujifilm X-S20 and X-T5, advocating for a thoughtful approach to camera configuration rather than relying on presets, which are often designed for casual use.&lt;/p&gt;
&lt;p&gt;Watch the video linked for specific adjustments and in-depth corrections on these issues.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://fstoppers.com/education/seven-fujifilm-settings-should-check-carefully-904652&quot;&gt;Continue reading on &lt;strong&gt;fstoppers.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/evolving-programming-languages-in-the-ai-era</id>
    <title>🔗 Evolving programming languages in the AI era</title>
    <updated>2026-10-22T08:00:00Z</updated>
    <published>2026-10-22T08:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/evolving-programming-languages-in-the-ai-era"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;How will programming languages evolve as coding agents begin to dominate the coding landscape? José Valim argues that this shift will require a reevaluation of communities, ecosystems, and programming language design.  The article is divided into two parts: Reflections and Agentic Tooling. In his reflections, Valim questions the impact of agents on programming communities and their frameworks, noting that agent-driven coding could diminish collaborative efforts. He highlights the need for languages to enhance guarantees while considering ergonomics for agents. Meanwhile, the section on Agentic tooling proposes innovative approaches to programming, emphasizing stronger user constraints and runtime observability over traditional debuggers. This thought-provoking piece suggests that as coding agents take the helm, the industry must adapt to new paradigms of programming, ensuring tools remain effective and purposeful.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://dashbit.co/blog/evolving-ai-era&quot;&gt;Continue reading on &lt;strong&gt;dashbit.co&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/elixir&quot;&gt;#elixir&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/background-jobs-with-oban-part-1-stop-blocking-your-liveview</id>
    <title>🔗 Background jobs with Oban, part 1: stop blocking your LiveView</title>
    <updated>2026-10-21T17:00:00Z</updated>
    <published>2026-10-21T17:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/background-jobs-with-oban-part-1-stop-blocking-your-liveview"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;A slow HTTP fetch can freeze your LiveView, impacting user experience. This article illustrates how to offload this slow job using Oban, a job processing library for Elixir. The piece guides readers through integrating Oban into a Phoenix application by setting up a jobs table, configuring queues, and writing a worker to handle tasks in the background. By transitioning slow processes out of the LiveView event handler, the app becomes more responsive, allowing users to interact without delays when processing links. The author provides a thorough code walkthrough, ensuring that developers can follow along with their own implementations. This approach optimizes performance and enhances user satisfaction by minimizing wait times during web page fetching.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://potions.io/blog/background-jobs-with-oban-part-1&quot;&gt;Continue reading on &lt;strong&gt;potions.io&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/elixir&quot;&gt;#elixir&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/phoenix&quot;&gt;#phoenix&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/devops&quot;&gt;#devops&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/testing&quot;&gt;#testing&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/when-a-pull-request-balloons-extract</id>
    <title>🔗 When a pull request balloons, extract</title>
    <updated>2026-10-21T13:00:00Z</updated>
    <published>2026-10-21T13:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/when-a-pull-request-balloons-extract"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;A Pull Request should never exceed 3,000 lines. Mike Zornek highlights his challenges with a feature for arbitrary refunds on an order. After successfully merging the first two phases of his implementation, Zornek faced overwhelming complexity with the UI in phase three, causing his Pull Request to balloon. Rather than merging a massive PR that risks getting missed during review, he extracted smaller components into manageable PRs. He pulled out unrelated bug fixes and additional test coverage to simplify the review process. This approach allows for clearer feedback and greater confidence when deploying code, especially for features that are critical for handling financial transactions. Extracting improvements from a large PR can be time-consuming but ultimately pays off in quality and maintainability.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://mikezornek.com/posts/2026/9/when-a-pull-request-balloons-extract/&quot;&gt;Continue reading on &lt;strong&gt;mikezornek.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/testing&quot;&gt;#testing&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/git&quot;&gt;#git&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/rip-commercial-ocr-an-open-source-model-just-topped-every-benchmark</id>
    <title>🔗 RIP commercial OCR. An open-source model just topped every benchmark.</title>
    <updated>2026-10-21T08:00:00Z</updated>
    <published>2026-10-21T08:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/rip-commercial-ocr-an-open-source-model-just-topped-every-benchmark"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Chandra OCR 2, a model from Brooklyn's Datalab, recently made waves in the OCR realm. With just 4 billion parameters, it outperformed both GPT-4o and Gemini across 90 languages, showcasing a remarkable ability to decode complex layouts and produce structured formats like Markdown and HTML. In independent benchmarks, it scored a staggering 85.9%, highlighting a clear edge over well-known models, diverging from typical OCR tools that struggle with intricacies like handwritten notes or multi-column documents. The article by Sumit Pandey emphasizes the implications of Chandra's performance, suggesting it could reframe expectations in document processing pipelines and beyond. The model’s open-source nature and easy accessibility, paired with its remarkable accuracy, means it’s poised to be a game-changer for anyone working with tricky, real-world documents.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://freedium-mirror.cfd/https%3A%2F%2Fwww.towardsdeeplearning.com%2Frip-commercial-ocr-an-open-source-model-just-topped-every-benchmark-351bf9d6be10&quot;&gt;Continue reading on &lt;strong&gt;freedium-mirror.cfd&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/tools&quot;&gt;#tools&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/8-chunking-strategies-for-rag-based-agents</id>
    <title>🐥 8 Chunking strategies for RAG-based agents</title>
    <updated>2026-10-20T17:00:00Z</updated>
    <published>2026-10-20T17:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/8-chunking-strategies-for-rag-based-agents"/>
    <content type="html">&lt;p&gt;&lt;img src=&quot;https://www.yellowduck.be/media/88462ca4-b0a1-4441-9537-59dae9d5ef65_2484x3002.webp&quot; alt=&quot;8 Chunking strategies for RAG-based agents&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Fixed-Si&lt;/strong&gt;ze: cuts text every fixed number of tokens, with a small overlap.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Recursive&lt;/strong&gt;: splits on paragraphs first, falling back to lines then words and characters only when a piece is still too big.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Document-Based&lt;/strong&gt;: splits wherever the document already marks a break, a heading, a chapter, an HTML tag, so a markdown file becomes one chunk per section on its own.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Semantic&lt;/strong&gt;: embeds each sentence, checks its similarity to the one before it, and starts a new chunk when that similarity drops. This finds real topic shifts instead of guessing by length.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LLM-Based&lt;/strong&gt;: asks an LLM to rewrite the text as standalone propositions, one idea per line, and turns each into a chunk, high quality but expensive across a large corpus.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agentic&lt;/strong&gt;: an AI agent reads each document, decides what it is (code, contract, table), and picks the chunking method for that file instead of one rule for everything.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Late Chunking&lt;/strong&gt;: runs the full document through an embedding model, then applies normal split boundaries and mean-pools within each boundary to create chunk embeddings.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hierarchical&lt;/strong&gt;: breaks the document into layers, sections, paragraphs, then sentences&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Courtesy of &lt;a href=&quot;https://newsletter.bytebytego.com/authors/bytebytego&quot;&gt;the ByteByteGo newsletter&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/langchain&quot;&gt;#langchain&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/what-is-the-difference-between-a-cto-and-a-vp-or-head-of-engineering</id>
    <title>🔗 What is the difference between a CTO and a VP or Head of Engineering?</title>
    <updated>2026-10-20T13:00:00Z</updated>
    <published>2026-10-20T13:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/what-is-the-difference-between-a-cto-and-a-vp-or-head-of-engineering"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;What distinguishes a CTO from a VP or Head of Engineering? This article clarifies their unique roles and responsibilities. A CTO focuses on enabling diverse technological outcomes, resembling a visionary strategist, while the VP or Head of Engineering is tasked with executing the current business strategy, maintaining a strong emphasis on product delivery. The piece underscores the importance of striking a balance between short- and long-term goals, showcasing examples from companies like Amazon and Microsoft. It argues that while titles may seem less significant in smaller organizations, they hold value for external recognition and relationships, particularly as careers evolve. Insightful reflections from the author highlight the dynamic nature of these roles and the impact of titles on professional journeys.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://code.dblock.org/2015/05/07/what-is-the-difference-between-cto-and-vp-or-head-of-engineering.html&quot;&gt;Continue reading on &lt;strong&gt;code.dblock.org&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/postgres-at-time-zone-utc-does-not-do-what-you-think-it-does</id>
    <title>🔗 Postgres AT TIME ZONE 'UTC' does not do what you think it does</title>
    <updated>2026-10-20T08:00:00Z</updated>
    <published>2026-10-20T08:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/postgres-at-time-zone-utc-does-not-do-what-you-think-it-does"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;AT TIME ZONE 'UTC'&lt;/code&gt; in Postgres may mislead developers into thinking it uniformly converts all timestamps to UTC, but this is not the case. Timestamps without time zones are converted directly to UTC, but timestamps with time zones are treated differently. Specifically, Postgres only adjusts their display according to the specified time zone rather than altering their actual values. This subtlety can lead to confusion in applications that handle time-sensitive data. Understanding this behavior is crucial for accurate time data manipulation in Postgres. Misinterpreting the conversion can result in significant issues in data reporting and analytics. Developers are advised to approach time zone conversions with care and ensure they grasp the nuances of time handling in Postgres, especially in complex systems that rely on accurate time tracking.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://bookofrevenue.com/blog/6ab81e9a97a13f0001f7e4e1/postgres-at-time-zone-u-does-not-do-what-you-think-it-does&quot;&gt;Continue reading on &lt;strong&gt;bookofrevenue.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/postgresql&quot;&gt;#postgresql&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/why-you-should-work-out-loud</id>
    <title>🔗 Why you should work out loud</title>
    <updated>2026-10-19T17:00:00Z</updated>
    <published>2026-10-19T17:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/why-you-should-work-out-loud"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;This article highlights the importance of documenting one's work instead of simply inventing content. It emphasizes that AI can assist in content creation, but it cannot replicate the depth of experiences accumulated over years. The author shares insights from Allen Buchanan, pointing out that the most compelling content often stems from lived experiences rather than imagination. The piece encourages creating a digital record of expertise, noting that with AI's capabilities to search and organize this knowledge, its value increases significantly over time. Steve Mays reflects on how his blog became a platform for capturing his professional journey, reminding readers of the power of shared experiences.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://www.smays.com/2026/09/work-out-loud/&quot;&gt;Continue reading on &lt;strong&gt;www.smays.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/measuring-ais-impact-on-delivery-speed</id>
    <title>🔗 Measuring AI’s impact on delivery speed</title>
    <updated>2026-10-19T13:00:00Z</updated>
    <published>2026-10-19T13:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/measuring-ais-impact-on-delivery-speed"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Is AI making software development faster? James Shore dives deep into quantifying this impact on delivery speed in his latest article. He emphasizes that while many believe productivity will rise with AI, the true measure lies in value produced relative to money spent. Delivery speed is merely a fraction of overall productivity, and distinguishing it from mere output is crucial. Shore outlines a rigorous approach to measurement that involves randomized controlled trials to retain accuracy amidst varied estimates and individual work styles. By doing so, he highlights the necessity of understanding what investments in AI truly yield for development teams, cautioning against superficial metrics. This insightful piece continues his series on AI's role in software development, paving the way for future discussions on unintended consequences and overall business outcomes.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://www.jamesshore.com/v2/blog/2026/measuring-ais-impact-on-delivery-speed&quot;&gt;Continue reading on &lt;strong&gt;www.jamesshore.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/what-i-believe-about-the-future-of-software-development</id>
    <title>🔗 What I believe about the future of software development</title>
    <updated>2026-10-19T08:00:00Z</updated>
    <published>2026-10-19T08:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/what-i-believe-about-the-future-of-software-development"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;What will the future hold for software development? Thorsten Ball boldly claims that the era of code review is already over, suggesting that humans will no longer find bugs in code generated by AI models. Instead, the focus will shift to reviewing system architecture. He anticipates the decline of unit tests, arguing that if models can produce flawless code, there's no need for safety checks. He also discusses how the traditional craft of coding may fade, making way for software that solves business problems more effectively. As we enter this post-binary era, the value of performance optimization will diminish. Tokens will redefine developer tools, making command line interfaces obsolete. Ball predicts that the roles within software development teams will dissolve, and economic access to software production will shift, leading to profound changes in the industry. This thought-provoking piece pushes readers to consider the future landscape of technology.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://thorstenball.com/blog/2026/09/19/what-i-believe-about-the-future-of-software-development/&quot;&gt;Continue reading on &lt;strong&gt;thorstenball.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/translating-country-codes-to-full-names-in-elixir-using-ex-cldr-countries</id>
    <title>🐥 Translating country codes to full names in Elixir using `ex_cldr_countries`</title>
    <updated>2026-10-18T17:00:00Z</updated>
    <published>2026-10-18T17:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/translating-country-codes-to-full-names-in-elixir-using-ex-cldr-countries"/>
    <content type="html">&lt;p&gt;Unlike JavaScript's &lt;code&gt;Intl.DisplayNames&lt;/code&gt;, Elixir doesn't provide built-in localization for country names. However, with the help of the excellent &lt;a href=&quot;https://hexdocs.pm/ex_cldr_countries/readme.html&quot;&gt;&lt;code&gt;ex_cldr_countries&lt;/code&gt;&lt;/a&gt; package, you can easily achieve dynamic, localized country translations backed by official Unicode CLDR data.&lt;/p&gt;
&lt;h1&gt;Installation&lt;/h1&gt;
&lt;p&gt;Add the package to your &lt;code&gt;mix.exs&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword-function&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;deps&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;    &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:ex_cldr_countries&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&amp;quot;~&amp;gt; 2.0&amp;quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;  &lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then fetch it:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-bash&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-function-call&quot;&gt;mix&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;deps.get&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Setting Up a Backend&lt;/h1&gt;
&lt;p&gt;Most examples online show calling &lt;code&gt;Cldr.Countries.from_iso/2&lt;/code&gt; directly, but modern &lt;code&gt;ex_cldr&lt;/code&gt; usage requires a &lt;strong&gt;backend module&lt;/strong&gt;. Without one, you're relying on a default backend that may not be configured — and you lose control over which locales get compiled in.&lt;/p&gt;
&lt;p&gt;Create a backend module in your app:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-comment&quot;&gt;# lib/my_app/cldr.ex&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;span class=&quot;l-keyword-function&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;MyApp.Cldr&lt;/span&gt; &lt;span class=&quot;l-keyword-function&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Cldr&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;    &lt;span class=&quot;l-string-special-symbol&quot;&gt;locales: &lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&amp;quot;en&amp;quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&amp;quot;fr&amp;quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&amp;quot;nl&amp;quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;    &lt;span class=&quot;l-string-special-symbol&quot;&gt;default_locale: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&amp;quot;en&amp;quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;    &lt;span class=&quot;l-string-special-symbol&quot;&gt;providers: &lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;l-module&quot;&gt;Cldr.Countries&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;locales&lt;/code&gt; list determines which translations get compiled into your app. Keep it to what you actually need — CLDR data is large and including everything will bloat your build.&lt;/p&gt;
&lt;h1&gt;Translating Country Codes&lt;/h1&gt;
&lt;p&gt;With the backend in place, translating a country code is straightforward:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-module&quot;&gt;MyApp.Cldr.Countries&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;from_iso&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:BE&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;locale: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&amp;quot;fr&amp;quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;span class=&quot;l-comment&quot;&gt;# =&amp;gt; {:ok, &amp;quot;Belgique&amp;quot;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;&lt;span class=&quot;l-module&quot;&gt;MyApp.Cldr.Countries&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;from_iso&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:NL&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;locale: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&amp;quot;en&amp;quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;&lt;span class=&quot;l-comment&quot;&gt;# =&amp;gt; {:ok, &amp;quot;Netherlands&amp;quot;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;&lt;span class=&quot;l-module&quot;&gt;MyApp.Cldr.Countries&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;from_iso&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:DE&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;locale: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&amp;quot;nl&amp;quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;&lt;span class=&quot;l-comment&quot;&gt;# =&amp;gt; {:ok, &amp;quot;Duitsland&amp;quot;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;The first argument is the ISO 3166-1 Alpha-2 code as an atom (&lt;code&gt;:BE&lt;/code&gt;, &lt;code&gt;:NL&lt;/code&gt;, &lt;code&gt;:DE&lt;/code&gt;, …)&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;locale&lt;/code&gt; option sets the output language&lt;/li&gt;
&lt;li&gt;If the translation can't be found, it returns an &lt;code&gt;{:error, reason}&lt;/code&gt; tuple, so it's safe to pattern match on&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Why This Approach&lt;/h1&gt;
&lt;p&gt;The backend module gives you three concrete benefits:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Scoped locale compilation&lt;/strong&gt; — only the locales you list get compiled, keeping your build lean&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Explicit and testable&lt;/strong&gt; — the backend is a real module you can reference directly in tests&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No global state surprises&lt;/strong&gt; — the bare &lt;code&gt;Cldr.*&lt;/code&gt; namespace is just a shim that delegates to whichever backend is configured as the default; using your own backend is always more predictable&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For most Phoenix applications, dropping this into a &lt;code&gt;lib/my_app/cldr.ex&lt;/code&gt; file is all the setup you need.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/elixir&quot;&gt;#elixir&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/clean-code-is-not-the-same-as-clear-code-comments-were-never-the-problem</id>
    <title>🔗 Clean code is not the same as clear code: comments were never the problem</title>
    <updated>2026-10-18T13:00:00Z</updated>
    <published>2026-10-18T13:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/clean-code-is-not-the-same-as-clear-code-comments-were-never-the-problem"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Why does clean code sometimes leave developers puzzled? In a thought-provoking article, Giorgi Kobaidze argues that comments are essential for understanding complex logic in code. He draws a parallel between road signs and code, emphasizing that even well-structured code can benefit from clarifications. Kobaidze addresses common misconceptions about comments, stating they aren't a sign of weakness but a helpful guide. Good comments translate dense code, explain decisions, and prevent confusion for other developers. He warns against relying solely on commit messages for explanations, noting how easily important information can be lost over time. The piece concludes with advice on how to comment effectively, highlighting the fine line between useful annotations and clutter.&lt;/p&gt;
&lt;p&gt;Overall, the article provides invaluable insights for developers looking to enhance code readability and maintainability without sacrificing the clarity that comments can bring.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://dev.to/georgekobaidze/clean-code-is-not-the-same-as-clear-code-comments-were-never-the-problem-42n?via=dailydev&quot;&gt;Continue reading on &lt;strong&gt;dev.to&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/virtual-environments-in-python</id>
    <title>🔗 Virtual environments in Python</title>
    <updated>2026-10-18T08:00:00Z</updated>
    <published>2026-10-18T08:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/virtual-environments-in-python"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Virtual environments are essential for managing Python projects effectively. The recommended tool is &lt;code&gt;uv&lt;/code&gt;, which not only manages project dependencies but also creates isolated environments. This article details how to use &lt;code&gt;uv&lt;/code&gt; to initialize projects, add packages, and automate virtual environment management. Operating outside of &lt;code&gt;uv&lt;/code&gt; usually means diving into the lower-level workings of virtual environments. It explains creating, activating, and deactivating environments along with checking their status. Readers get practical commands and examples to manage packages, including the usage of &lt;code&gt;pyproject.toml&lt;/code&gt; and &lt;code&gt;requirements.txt&lt;/code&gt;. The guide emphasizes the importance of isolation and package management to avoid conflicts in project dependencies. Simply put, understanding these concepts allows developers to maintain clean and organized Python environments for their projects.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://tiangolo.com/guides/virtual-environments/&quot;&gt;Continue reading on &lt;strong&gt;tiangolo.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/python&quot;&gt;#python&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/testing-async-python-without-losing-your-mind</id>
    <title>🔗 Testing async Python without losing your mind</title>
    <updated>2026-10-17T17:00:00Z</updated>
    <published>2026-10-17T17:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/testing-async-python-without-losing-your-mind"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Async testing in Python can be challenging, yet it's not as daunting when you understand key concepts. The article by Peyton Green dives into common pitfalls like event loop scope mismatches and intricate fixture lifecycle issues that often lead to confusion during testing. With detailed examples, it discusses the critical differences between using &lt;code&gt;pytest-asyncio&lt;/code&gt; and &lt;code&gt;anyio&lt;/code&gt;, showing how to set up async tests properly and avoid frequent errors. Green emphasizes the importance of matching fixture scopes with event loops to ensure smooth operation. This guide not only highlights these common errors but also provides concrete solutions, making async testing approachable even for those new to the framework. Ultimately, readers gain a clear understanding of testing async code through insightful strategies for FastAPI endpoints and handling background tasks effectively, all aimed at streamlining the testing process.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://dev.to/peytongreen_dev/testing-async-python-without-losing-your-mind-5344&quot;&gt;Continue reading on &lt;strong&gt;dev.to&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/python&quot;&gt;#python&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/testing&quot;&gt;#testing&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/natural-sorting-in-postgresql-mastering-custom-collations</id>
    <title>🔗 Natural sorting in PostgreSQL: mastering custom collations</title>
    <updated>2026-10-17T13:00:00Z</updated>
    <published>2026-10-17T13:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/natural-sorting-in-postgresql-mastering-custom-collations"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Sorting alphanumeric data usually follows character code points, which can lead to confusing results for numbers. This article reveals how to implement &amp;quot;natural&amp;quot; sorting in Postgres, allowing numbers to be sorted by value rather than code point. It explains creating custom collation objects using the &lt;code&gt;CREATE COLLATION&lt;/code&gt; command, focusing on useful examples for numeric ordering. The author clarifies that collations determine the sort order of characters, specifically beneficial when handling alphanumeric strings. With insights about the ICU provider and locale definitions, this guide offers important context for when developers need to sort mixed data types effectively. The hands-on approach culminates in applying the collation to enhance the sorting syntax, making Postgres a powerful tool in better organizing data.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://testdouble.com/insights/natural-sorting-postgres-collation&quot;&gt;Continue reading on &lt;strong&gt;testdouble.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/database&quot;&gt;#database&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/postgresql&quot;&gt;#postgresql&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/python-3-15-preview-lazy-imports</id>
    <title>🔗 Python 3.15 Preview: Lazy Imports</title>
    <updated>2026-10-17T08:00:00Z</updated>
    <published>2026-10-17T08:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/python-3-15-preview-lazy-imports"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Python 3.15 adds lazy imports: a lazy keyword that puts off loading a module until the first time you use it. The import statement stays where it belongs, at the top of your file, but the work of running it moves to the moment something touches the module’s name. In this tutorial, you’ll make a command-line tool start more than twice as fast without moving a single import.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://realpython.com/python315-lazy-imports/&quot;&gt;Continue reading on &lt;strong&gt;realpython.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/python&quot;&gt;#python&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>https://www.yellowduck.be/posts/python-3-15-is-bigger-than-you-think-7-new-features-worth-using-in-2026</id>
    <title>🔗 Python 3.15 is bigger than you think: 7 new features worth using in 2026</title>
    <updated>2026-10-16T17:00:00Z</updated>
    <published>2026-10-16T17:00:00Z</published>
    <author><name>Pieter Claerhout</name><email>pieter@yellowduck.be</email></author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/python-3-15-is-bigger-than-you-think-7-new-features-worth-using-in-2026"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Python 3.15 brings significant enhancements beyond typical library improvements. It introduces important features like &lt;code&gt;lazy import&lt;/code&gt;, which allows deferring module loading until needed, and the &lt;code&gt;frozendict&lt;/code&gt;, providing a new immutable mapping type. The &lt;code&gt;sentinel()&lt;/code&gt; function replaces the &lt;code&gt;_MISSING&lt;/code&gt; trick for optional values, helping to clarify function parameters. Comprehensions can now directly unpack iterables, simplifying data processing tasks. These features not only simplify code but also enhance performance, making them valuable for developers looking to future-proof their Python projects. With the release still in prerelease, now is an ideal time to explore these updates without affecting production systems. Expect improved readability and maintainability, catering to modern Python practices and enhancing overall coding efficiency.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://freedium-mirror.cfd/https://captain-solaris.medium.com/python-3-15-is-bigger-than-you-think-7-new-features-worth-using-in-2026-0235523fbc03&quot;&gt;Continue reading on &lt;strong&gt;freedium-mirror.cfd&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/python&quot;&gt;#python&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
</feed>
