<?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">
  <link href="https://www.yellowduck.be" rel="alternate"/>
  <link href="https://www.yellowduck.be/posts/feed" rel="self"/>
  <author>
    <name>Pieter Claerhout</name>
    <email>pieter@yellowduck.be</email>
  </author>
  <id>https://www.yellowduck.be/posts/feed</id>
  <title>🐥 YellowDuck.be</title>
  <updated>2026-06-03T17:00:00Z</updated>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/computer-use-is-45x-more-expensive-than-structured-apis" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;A benchmark study shows that AI agents using computer vision to navigate web interfaces are 45 times more expensive than those using structured APIs, largely due to higher token consumption and performance variance. While vision-based agents are still necessary for third-party software, auto-generating API surfaces for internal tools provides a far more efficient and cost-effective deployment strategy.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://reflex.dev/blog/computer-use-is-45x-more-expensive-than-structured-apis/&quot;&gt;Continue reading on &lt;strong&gt;reflex.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/tools&quot;&gt;#tools&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>
    <published>2026-06-03T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/computer-use-is-45x-more-expensive-than-structured-apis</id>
    <title>🔗 Computer use is 45x more expensive than structured APIs</title>
    <updated>2026-06-03T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/why-developers-should-and-shouldnt-use-llms-in-our-development" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Why should I use AI? This pressing question is at the forefront of our evolving software development landscape. In the wake of LLMs like ChatGPT, developers must decide how to harness this technology effectively. While LLMs excel at handling repetitive tasks or producing prototypes quickly, they can compound security risks and tech debt if misused. I found that the clients I work with sometimes desire AI solutions even when they&apos;re unnecessary. This muddles our understanding of their actual needs, highlighting the importance of expert insight over flashy trends. I’ve noticed the most significant shifts in our workflow center on using LLMs to free developers from rote tasks, allowing us to focus on creative problem-solving instead. However, caution can&apos;t be overlooked: without proper review, LLM-generated code can lead to serious vulnerabilities.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://tighten.com/insights/pragmatic-ai-why-devs-should-and-shouldnt-use-llms/&quot;&gt;Continue reading on &lt;strong&gt;tighten.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>
    <published>2026-06-03T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/why-developers-should-and-shouldnt-use-llms-in-our-development</id>
    <title>🔗 Why developers should – and shouldn’t – use LLMs in our development</title>
    <updated>2026-06-03T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/the-robots-are-replacing-the-packages" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;The way AI-assisted development is changing how developers handle package installations is striking. Manual coding may have taken time, but with intelligent agents automating the background work, developers can focus on high-level tasks. The article categorizes common scenarios developers face in package decision-making: shared problems where robots excel, complex issues requiring thorough modeling, external dependencies needing ongoing maintenance, and opinionated frameworks shaping project direction. By understanding which problems to delegate to robots versus those that require human oversight, developers make smarter choices. The piece emphasizes a shift from finding solutions to knowing whether one wants to own a particular problem, marking a significant evolution in software development practices.&lt;/p&gt;
&lt;p&gt;Robots are taking over package handling, letting us focus on complex problems. Discover the four categories that define when to leverage this AI power.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://spatie.be/blog/the-robots-are-replacing-the-packages&quot;&gt;Continue reading on &lt;strong&gt;spatie.be&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>
    <published>2026-06-03T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/the-robots-are-replacing-the-packages</id>
    <title>🔗 The robots are replacing the packages</title>
    <updated>2026-06-03T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/why-crypto-getrandomvalues-matters-in-javascript" rel="alternate"/>
    <content type="html">&lt;p&gt;Generating random values sounds simple, until you need randomness that is actually secure.&lt;/p&gt;
&lt;p&gt;A lot of JavaScript developers reach for &lt;code&gt;Math.random()&lt;/code&gt; out of habit. While that works fine for visual effects, games, or non-critical IDs, it should never be used for anything security-sensitive.&lt;/p&gt;
&lt;p&gt;That’s where the Web Crypto API comes in.&lt;/p&gt;
&lt;h1&gt;The problem with &lt;code&gt;Math.random()&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;Math.random()&lt;/code&gt; is not cryptographically secure.&lt;/p&gt;
&lt;p&gt;Its output is deterministic and predictable enough that an attacker may be able to reproduce or guess generated values under certain conditions.&lt;/p&gt;
&lt;p&gt;That makes it unsuitable for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Session tokens&lt;/li&gt;
&lt;li&gt;Password reset links&lt;/li&gt;
&lt;li&gt;API keys&lt;/li&gt;
&lt;li&gt;CSRF tokens&lt;/li&gt;
&lt;li&gt;Encryption keys&lt;/li&gt;
&lt;li&gt;Secure identifiers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-javascript&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;type-builtin&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;36&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This may look random, but it is not secure.&lt;/p&gt;
&lt;h1&gt;Using &lt;code&gt;crypto.getRandomValues()&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;Modern browsers provide a secure random number generator through the Web Crypto API.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-javascript&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;bytes&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword-operator&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;constructor&quot;&gt;Uint8Array&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;2&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;3&quot;&gt;&lt;span class=&quot;variable&quot;&gt;crypto&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;getRandomValues&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;4&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;5&quot;&gt;&lt;span class=&quot;variable-builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This fills the typed array with cryptographically secure random bytes provided by the operating system.&lt;/p&gt;
&lt;h1&gt;Generating a secure random token&lt;/h1&gt;
&lt;p&gt;A common use case is generating secure tokens or identifiers.&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-javascript&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;keyword-function&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;function&quot;&gt;generateToken&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable-parameter&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation-bracket&quot;&gt;&amp;lbrace;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;bytes&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword-operator&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;constructor&quot;&gt;Uint8Array&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;3&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;4&quot;&gt;  &lt;span class=&quot;variable&quot;&gt;crypto&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;getRandomValues&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;5&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;6&quot;&gt;  &lt;span class=&quot;keyword-return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;type-builtin&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;7&quot;&gt;    &lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable-parameter&quot;&gt;byte&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;padStart&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;8&quot;&gt;    &lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;9&quot;&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;&amp;rbrace;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;10&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;11&quot;&gt;&lt;span class=&quot;variable-builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;function-call&quot;&gt;generateToken&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example output:&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;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;4f8b7d3a1f9e0c8d7a2b6c5d4e3f1a9c
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is significantly safer than using &lt;code&gt;Math.random()&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;Typed arrays are required&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;crypto.getRandomValues()&lt;/code&gt; only works with integer-based typed arrays.&lt;/p&gt;
&lt;p&gt;Supported examples include:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-javascript&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;keyword-operator&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;constructor&quot;&gt;Uint8Array&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;2&quot;&gt;&lt;span class=&quot;keyword-operator&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;constructor&quot;&gt;Uint16Array&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;3&quot;&gt;&lt;span class=&quot;keyword-operator&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;constructor&quot;&gt;Int32Array&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will fail:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-javascript&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;variable&quot;&gt;crypto&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;getRandomValues&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because regular JavaScript arrays are not supported.&lt;/p&gt;
&lt;h1&gt;Browser and runtime support&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;crypto.getRandomValues()&lt;/code&gt; is widely supported in modern browsers.&lt;/p&gt;
&lt;p&gt;It is also available in runtimes like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Node.js&lt;/li&gt;
&lt;li&gt;Deno&lt;/li&gt;
&lt;li&gt;Bun&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example in Node.js:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-javascript&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;bytes&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword-operator&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;constructor&quot;&gt;Uint8Array&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;2&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;3&quot;&gt;&lt;span class=&quot;variable&quot;&gt;crypto&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;getRandomValues&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;4&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;5&quot;&gt;&lt;span class=&quot;variable-builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In older Node.js versions, developers typically used:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-javascript&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;function-builtin&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&quot;crypto&quot;&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;randomBytes&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;UUID generation&lt;/h1&gt;
&lt;p&gt;If your goal is generating UUIDs, modern runtimes also support:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-javascript&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;variable&quot;&gt;crypto&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;randomUUID&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example:&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;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;550e8400-e29b-41d4-a716-446655440000
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Internally, this also uses cryptographically secure randomness.&lt;/p&gt;
&lt;h1&gt;Things to remember&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;Math.random()&lt;/code&gt; for non-security-related randomness only&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;crypto.getRandomValues()&lt;/code&gt; for anything security-sensitive&lt;/li&gt;
&lt;li&gt;Prefer &lt;code&gt;crypto.randomUUID()&lt;/code&gt; when generating UUIDs&lt;/li&gt;
&lt;li&gt;Always generate randomness using the operating system’s secure RNG&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For modern JavaScript applications, &lt;code&gt;crypto.getRandomValues()&lt;/code&gt; should be the default choice whenever security matters.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/javascript&quot;&gt;#javascript&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/auth&quot;&gt;#auth&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-06-02T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/why-crypto-getrandomvalues-matters-in-javascript</id>
    <title>🐥 Why crypto.getRandomValues() matters in JavaScript</title>
    <updated>2026-06-02T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/the-language-of-the-boundary" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;The article discusses the development journey of creating a JSON Schema validator library for Elixir, detailing the complexities encountered when integrating JSON schema validation with Open API Spex. It emphasizes the necessity of validating data at boundaries, where untrusted external data is handled. The author, reflecting on past experiences, distinguishes between validating solely within Elixir and creating a shared understanding through JSON Schema among various programming teams, particularly in relation to Python and TypeScript. In exploring the challenges of maintaining and implementing validation rules effectively, the piece advocates for treating validation rules as data rather than as code, highlighting the importance of schemas as a means of ensuring compatibility and accuracy across different technologies. Ultimately, the author champions JSON Schema as the preferred solution for establishing clear, functional boundaries in application development.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://lud.codes/blog/why-i-wrote-a-json-schema-validation-library-for-elixir/&quot;&gt;Continue reading on &lt;strong&gt;lud.codes&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/elixir&quot;&gt;#elixir&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-06-02T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/the-language-of-the-boundary</id>
    <title>🔗 The language of the boundary</title>
    <updated>2026-06-02T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/ai-agents-keep-failing-the-fix-is-40-years-old" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;AI agents often falter due to hidden dependencies in the code, leading to failures in production environments. This issue stems not from the models themselves but from the tight coupling and mutable states in the codebase that AI agents cannot effectively navigate. As Cyrus Radfar points out, while human developers build mental models over time, AI lacks that context, resulting in complicating hidden states that cause cascading errors. To remedy this, he suggests applying long-established principles of functional programming from the 1980s. By ensuring that all functions are pure, with explicit data flows and minimal side effects, AI agents can operate more reliably in scenarios where codebases contain numerous dependencies. This approach enables agents to modify functions without facing the risk of unintended consequences from hidden global states. The implementation of these principles could significantly enhance AI-driven development practices.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://cyrusradfar.com/thoughts/functional-programming-is-the-only-way-to-scale-with-ai&quot;&gt;Continue reading on &lt;strong&gt;cyrusradfar.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>
    <published>2026-06-02T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/ai-agents-keep-failing-the-fix-is-40-years-old</id>
    <title>🔗 AI agents keep failing. The fix is 40 years old.</title>
    <updated>2026-06-02T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/claude-code-is-a-build-system-not-a-chatbot" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Claude Code redefines the interaction with AI coding by treating it as a build system rather than merely a chatbot. After a year of iteration, the author emphasizes the importance of treating AI like a configurable, layered, and observable engineering system. By moving from passive to active enforcement of coding standards and best practices, projects become less prone to errors, especially when attention drifts. The article explains how to set up automatic checks, the significance of writing standards clearly, and the benefits of using specialized subagents for tasks, plus future-proofing code through systematic hooks. With insights from the author&apos;s experiences across various programming languages, this piece serves as a guide to enhance AI-driven development and maintain consistency and quality in coding practices over time.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://vinny.dev/blog/2026-04-25-claude-code-build-system/&quot;&gt;Continue reading on &lt;strong&gt;vinny.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/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/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>
    <published>2026-06-01T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/claude-code-is-a-build-system-not-a-chatbot</id>
    <title>🔗 Claude Code is a build system, not a chatbot</title>
    <updated>2026-06-01T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/functional-programmers-need-to-take-a-look-at-zig" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;What if a programming language could change the way functional programmers think about system memory management? In this article, the author examines how Zig introduces innovative memory management solutions that could appeal to functional programmers tired of traditional approaches.&lt;/p&gt;
&lt;p&gt;They dive into the unique aspects of Zig, particularly its &lt;code&gt;comptime&lt;/code&gt; feature, which simplifies type-system programming. The author argues against the reliance on garbage collection in existing functional languages, noting that it hampers performance and obscures the underlying machine language of programming.&lt;/p&gt;
&lt;p&gt;With examples illustrating Zig&apos;s capabilities, the article showcases how this language not only improves performance but also allows developers to express their ideas more clearly and effectively. Zig’s design choices encourage deeper understanding of systems programming while maintaining the benefits of functional programming principles.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://pure-systems.org/posts/2026-04-29-functional-programmers-need-to-take-a-look-at-zig.html&quot;&gt;Continue reading on &lt;strong&gt;pure-systems.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;a href=&quot;https://www.yellowduck.be/tags/pattern&quot;&gt;#pattern&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>
    <published>2026-06-01T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/functional-programmers-need-to-take-a-look-at-zig</id>
    <title>🔗 Functional programmers need to take a look at Zig</title>
    <updated>2026-06-01T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/api-versioning-should-be-your-last-resort" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Versioning doesn&apos;t have to be the go-to solution when evolving an API. Instead, focusing on effective change management can lead to better API longevity. This article emphasizes that &lt;strong&gt;API versioning is a compatibility tool, not a design strategy&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It outlines how many API changes can be managed without resorting to creating new versions. Key principles include keeping existing fields intact, ensuring optional data remains optional, and incorporating new operations instead of modifying old ones. The discussion delves into how breaking changes happen beyond just URL modifications, bringing attention to aspects like request validation and operational shifts.&lt;/p&gt;
&lt;p&gt;Overall, this piece encourages a mindset shift towards compatibility and thoughtful management rather than adhering to versioning as a default approach.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://www.milanjovanovic.tech/blog/api-versioning-should-be-your-last-resort&quot;&gt;Continue reading on &lt;strong&gt;www.milanjovanovic.tech&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>
    <published>2026-06-01T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/api-versioning-should-be-your-last-resort</id>
    <title>🔗 API versioning should be your last resort</title>
    <updated>2026-06-01T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/how-to-find-all-jira-issues-ever-assigned-to-someone-even-historical-ones" rel="alternate"/>
    <content type="html">&lt;p&gt;If you&apos;ve ever tried to pull up all the tickets that &lt;strong&gt;Sarah Mitchell&lt;/strong&gt; worked on last year, you&apos;ve probably started with the obvious query:&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;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;assignee = &quot;sarah.mitchell@example.com&quot;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then realised it only shows her &lt;em&gt;current&lt;/em&gt; tickets — not the ones she handed off to &lt;strong&gt;Tom Bergkamp&lt;/strong&gt; or closed six months ago.&lt;/p&gt;
&lt;p&gt;Here&apos;s the fix.&lt;/p&gt;
&lt;h1&gt;The &lt;code&gt;was&lt;/code&gt; operator&lt;/h1&gt;
&lt;p&gt;Jira&apos;s JQL has a &lt;code&gt;was&lt;/code&gt; operator that checks the &lt;strong&gt;change history&lt;/strong&gt; of a field, not just its current value. So to find every issue Sarah was ever assigned to:&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;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;assignee was &quot;sarah.mitchell@example.com&quot;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Simple, but powerful.&lt;/p&gt;
&lt;h1&gt;Checking multiple people at once&lt;/h1&gt;
&lt;p&gt;Need to audit the work of an entire sub-team? Use &lt;code&gt;was in&lt;/code&gt; with a list:&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;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;assignee was in (&quot;sarah.mitchell@example.com&quot;, &quot;tom.bergkamp@example.com&quot;, &quot;priya.nair@example.com&quot;)
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This returns any issue that was assigned to &lt;em&gt;any&lt;/em&gt; of those three people at any point in time.&lt;/p&gt;
&lt;h1&gt;Scoping to a specific year&lt;/h1&gt;
&lt;p&gt;Combine it with the &lt;code&gt;during&lt;/code&gt; clause to limit results to a time window — useful for annual reviews, retrospectives, or handover audits:&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;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;assignee was in (&quot;sarah.mitchell@example.com&quot;, &quot;tom.bergkamp@example.com&quot;) during (&quot;2025-01-01&quot;, &quot;2025-12-31&quot;)
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This only matches issues where one of them was the assignee &lt;em&gt;at some point during 2025&lt;/em&gt; — even if the ticket has since been reassigned or closed.&lt;/p&gt;
&lt;h1&gt;Putting it all together&lt;/h1&gt;
&lt;p&gt;A full, practical query might look 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;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;project = &quot;PLATFORM&quot;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;2&quot;&gt;AND assignee was in (&quot;sarah.mitchell@example.com&quot;, &quot;tom.bergkamp@example.com&quot;)
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;3&quot;&gt;AND during (&quot;2025-01-01&quot;, &quot;2025-12-31&quot;)
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;4&quot;&gt;ORDER BY updated DESC
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;was&lt;/code&gt; operator works on other fields too — &lt;code&gt;status was &quot;In Progress&quot;&lt;/code&gt;, &lt;code&gt;priority was &quot;High&quot;&lt;/code&gt;, etc. — so it&apos;s worth keeping in your JQL toolkit whenever you need to query history rather than current state.&lt;/p&gt;&lt;p&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/best-practice&quot;&gt;#best-practice&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-05-31T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/how-to-find-all-jira-issues-ever-assigned-to-someone-even-historical-ones</id>
    <title>🐥 How to find all Jira issues ever assigned to someone (even historical ones)</title>
    <updated>2026-05-31T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/how-i-write-http-servers" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;No one enjoys redundant tasks, yet composing and testing HTTP servers from scratch often leads to repetition. In this article, Blain Smith shares his experience crafting HTTP servers, focusing on creating REST APIs with vital features like JSON/XML support and authorization tokens. He emphasizes the importance of starting from individual handlers rather than the main server, which aids in identifying dependencies.&lt;/p&gt;
&lt;p&gt;The article also dives into setting up request/response lifecycle tests without needing an actual server. By employing middleware for logging and authorization, he showcases how each component can be efficiently layered, making handlers both functional and secure. This approach not only promotes cleaner code but also lays the groundwork for robust server architectures.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://blainsmith.com/articles/how-i-write-http-servers/&quot;&gt;Continue reading on &lt;strong&gt;blainsmith.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/golang&quot;&gt;#golang&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/http&quot;&gt;#http&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/testing&quot;&gt;#testing&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-05-31T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/how-i-write-http-servers</id>
    <title>🔗 How I write HTTP servers</title>
    <updated>2026-05-31T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/i-dont-want-your-prs-anymore" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;With the rise of LLMs, code contributions are losing their value. The author articulates a stance against merging pull requests (PRs) because of concerns about malicious intent and the escalating complexity of code reviews. Instead, the article suggests alternative methods for collaboration which include providing feedback, discussing ideas, and reporting bugs. It highlights that while code writing is becoming automated, the need for meaningful insights and unique perspectives remains crucial. Rather than submitting PRs, contributors are encouraged to fork projects, prototype changes, and share their findings. This shift emphasizes a more collaborative and efficient approach to software development, reflecting the evolving landscape of coding and maintenance.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://dpc.pw/posts/i-dont-want-your-prs-anymore/&quot;&gt;Continue reading on &lt;strong&gt;dpc.pw&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;a href=&quot;https://www.yellowduck.be/tags/github&quot;&gt;#github&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-05-31T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/i-dont-want-your-prs-anymore</id>
    <title>🔗 I don&apos;t want your PRs anymore</title>
    <updated>2026-05-31T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/the-age-of-refinement-optimizing-search-performance-for-mcp-systems" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Search isn’t finished when it works; it’s finished when you can prove it works—and refine it when it doesn’t. Christoph Beck’s article on search performance optimization for MCP systems highlights the importance of crafting good questions for effective search evaluation. This involves creating a dataset of specific queries tied to relevant document chunks, allowing teams to measure and improve their search strategies accurately. By focusing on metrics like precision and recall, Beck explains how to transition search from a black box to an iterative, improvable system, ultimately enhancing user experience and system reliability. His insights make it clear that understanding the nuances of search can significantly impact overall performance in MCP environments.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://bitcrowd.dev/the-age-of-refinement-evaluating-optimizing-search-performance-mcp-systems/&quot;&gt;Continue reading on &lt;strong&gt;bitcrowd.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/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;a href=&quot;https://www.yellowduck.be/tags/testing&quot;&gt;#testing&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-05-30T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/the-age-of-refinement-optimizing-search-performance-for-mcp-systems</id>
    <title>🔗 The age of refinement: optimizing search performance for MCP systems</title>
    <updated>2026-05-30T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/openapi-dsls-the-silent-developer-productivity-killer" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;I learned that relying on Domain-Specific Languages (DSLs) for OpenAPI documentation can lead to significant productivity pitfalls. In a recent experience with Elixir, I faced a convoluted mess when trying to manage my API specs embedded within controller logic. The DSL library imposed rigid structures, causing unnecessary complexity and confusion. The result? I had to switch back to a straightforward YAML specification, which not only simplified the process but also reduced errors when interacting with AI tools like ChatGPT. Without the added abstraction, the YAML format was easier to read, and it became apparent that the claimed benefits of the DSL didn&apos;t outweigh its drawbacks. In the end, simpler often means better.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://www.curiosum.com/blog/openapi-dsls&quot;&gt;Continue reading on &lt;strong&gt;www.curiosum.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/elixir&quot;&gt;#elixir&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-05-30T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/openapi-dsls-the-silent-developer-productivity-killer</id>
    <title>🔗 OpenAPI DSLs: The silent developer productivity killer</title>
    <updated>2026-05-30T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/10-lessons-for-agentic-coding" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Devs should use AI for fast implementation and automation while focusing their human expertise on testing, documentation, and high-value architectural decisions.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://www.dbreunig.com/2026/05/04/10-lessons-for-agentic-coding.html&quot;&gt;Continue reading on &lt;strong&gt;www.dbreunig.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;a href=&quot;https://www.yellowduck.be/tags/testing&quot;&gt;#testing&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-05-30T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/10-lessons-for-agentic-coding</id>
    <title>🔗 10 lessons for agentic coding</title>
    <updated>2026-05-30T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/downloading-external-images-as-squares-from-a-phoenix-app" rel="alternate"/>
    <content type="html">&lt;p&gt;A common need in admin tools: click a button, download a remote image as a square JPEG. Simple enough — until CORS gets in the way.&lt;/p&gt;
&lt;h1&gt;The CORS problem&lt;/h1&gt;
&lt;p&gt;When fetching a cross-origin image and drawing it onto a &lt;code&gt;&lt;canvas&gt;&lt;/code&gt;, the browser marks the canvas as &quot;tainted&quot;. The moment you call &lt;code&gt;canvas.toBlob()&lt;/code&gt; to read the pixel data back out, it throws a security error. Unless the image server sends explicit CORS headers — which most don&apos;t — you can&apos;t do canvas operations on cross-origin images.&lt;/p&gt;
&lt;h1&gt;The fix: a server-side proxy&lt;/h1&gt;
&lt;p&gt;The solution is to proxy the image through the Phoenix app. From the browser&apos;s point of view the image comes from the same origin, so the canvas stays clean.&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;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;keyword-function&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;function&quot;&gt;image_proxy&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;punctuation-special&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;&amp;lbrace;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&quot;url&quot;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;&amp;rbrace;&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;keyword&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&lt;-&lt;/span&gt; &lt;span class=&quot;function-call&quot;&gt;allowed_url?&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;3&quot;&gt;       &lt;span class=&quot;punctuation-bracket&quot;&gt;&amp;lbrace;&lt;/span&gt;&lt;span class=&quot;string-special-symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;&amp;rbrace;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&lt;-&lt;/span&gt; &lt;span class=&quot;module&quot;&gt;Req&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-call&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;4&quot;&gt;    &lt;span class=&quot;variable&quot;&gt;content_type&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;5&quot;&gt;      &lt;span class=&quot;variable&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;headers&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;6&quot;&gt;      &lt;span class=&quot;operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;module&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-call&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&quot;content-type&quot;&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;punctuation-bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&quot;image/jpeg&quot;&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;7&quot;&gt;      &lt;span class=&quot;operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;module&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-call&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;8&quot;&gt;      &lt;span class=&quot;operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;module&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-call&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&quot;;&quot;&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;9&quot;&gt;      &lt;span class=&quot;operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;function-call&quot;&gt;hd&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;10&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;11&quot;&gt;    &lt;span class=&quot;variable&quot;&gt;conn&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;12&quot;&gt;    &lt;span class=&quot;operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;function-call&quot;&gt;put_resp_content_type&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;content_type&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;13&quot;&gt;    &lt;span class=&quot;operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;function-call&quot;&gt;send_resp&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;14&quot;&gt;  &lt;span class=&quot;keyword&quot;&gt;else&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;15&quot;&gt;    &lt;span class=&quot;boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;function-call&quot;&gt;send_resp&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&quot;Invalid URL&quot;&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;16&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;function-call&quot;&gt;send_resp&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;502&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&quot;Failed to fetch image&quot;&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;17&quot;&gt;  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;18&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;allowed_url?/1&lt;/code&gt; function validates the host against a known allowlist — an important SSRF guard so the proxy can&apos;t be abused to fetch arbitrary internal URLs.&lt;/p&gt;
&lt;h1&gt;Center-cropping to a square in the browser&lt;/h1&gt;
&lt;p&gt;Once the image loads from the proxy, a small Canvas API snippet handles the crop. The logic is straightforward: use the shorter dimension as the square size, then offset into the longer dimension by half the difference to take the center slice.&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-javascript&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;variable-builtin&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&quot;phx:download-square-image&quot;&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable-parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;punctuation-bracket&quot;&gt;&amp;lbrace;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;punctuation-bracket&quot;&gt;&amp;lbrace;&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;punctuation-bracket&quot;&gt;&amp;rbrace;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable-member&quot;&gt;detail&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;3&quot;&gt;  &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;proxyUrl&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;`/image-proxy?url=&lt;/span&gt;&lt;span class=&quot;punctuation-special&quot;&gt;$&amp;lbrace;&lt;/span&gt;&lt;span class=&quot;function-builtin&quot;&gt;encodeURIComponent&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation-special&quot;&gt;&amp;rbrace;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;4&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;5&quot;&gt;  &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword-operator&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;constructor&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;6&quot;&gt;  &lt;span class=&quot;variable&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method&quot;&gt;onload&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;punctuation-bracket&quot;&gt;&amp;lbrace;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;7&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;type-builtin&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable-member&quot;&gt;naturalWidth&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable-member&quot;&gt;naturalHeight&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;8&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;canvas&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;variable-builtin&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&quot;canvas&quot;&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;9&quot;&gt;    &lt;span class=&quot;variable&quot;&gt;canvas&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable-member&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;10&quot;&gt;    &lt;span class=&quot;variable&quot;&gt;canvas&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable-member&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;11&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;12&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;canvas&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&quot;2d&quot;&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;13&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;offsetX&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable-member&quot;&gt;naturalWidth&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;14&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;offsetY&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable-member&quot;&gt;naturalHeight&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;15&quot;&gt;    &lt;span class=&quot;variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;drawImage&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;offsetX&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;offsetY&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;16&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;17&quot;&gt;    &lt;span class=&quot;variable&quot;&gt;canvas&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;toBlob&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable-parameter&quot;&gt;blob&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;punctuation-bracket&quot;&gt;&amp;lbrace;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;18&quot;&gt;      &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;variable-builtin&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;19&quot;&gt;      &lt;span class=&quot;variable&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable-member&quot;&gt;href&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;createObjectURL&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;blob&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;20&quot;&gt;      &lt;span class=&quot;variable&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable-member&quot;&gt;download&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;21&quot;&gt;      &lt;span class=&quot;variable&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;22&quot;&gt;      &lt;span class=&quot;constant&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-method-call&quot;&gt;revokeObjectURL&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable-member&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;23&quot;&gt;    &lt;span class=&quot;punctuation-bracket&quot;&gt;&amp;rbrace;&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&quot;image/jpeg&quot;&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0.95&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;24&quot;&gt;  &lt;span class=&quot;punctuation-bracket&quot;&gt;&amp;rbrace;&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;25&quot;&gt;  &lt;span class=&quot;variable&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable-member&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;proxyUrl&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;26&quot;&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;&amp;rbrace;&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For a landscape image the left and right edges are trimmed, keeping the center. For a portrait image the top and bottom are trimmed instead. The full shorter dimension is always preserved — no upscaling, no padding.&lt;/p&gt;
&lt;h1&gt;Wiring it up in LiveView&lt;/h1&gt;
&lt;p&gt;The download is triggered from the template using Phoenix&apos;s &lt;code&gt;JS.dispatch/2&lt;/code&gt;, which fires a custom DOM event with the image URL and filename as detail:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-heex&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;button&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;tag-attribute&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;cursor-pointer&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&quot;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;3&quot;&gt;  &lt;span class=&quot;tag-attribute&quot;&gt;phx-click&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;tag-delimiter&quot;&gt;&amp;lbrace;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;4&quot;&gt;    &lt;span class=&quot;module&quot;&gt;JS&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;function-call&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&quot;phx:download-square-image&quot;&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;5&quot;&gt;      &lt;span class=&quot;string-special-symbol&quot;&gt;detail: &lt;/span&gt;&lt;span class=&quot;punctuation-special&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;&amp;lbrace;&lt;/span&gt;&lt;span class=&quot;string-special-symbol&quot;&gt;url: &lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;image_url&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string-special-symbol&quot;&gt;filename: &lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&quot;image.jpg&quot;&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;&amp;rbrace;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;6&quot;&gt;    &lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;7&quot;&gt;  &lt;span class=&quot;tag-delimiter&quot;&gt;&amp;rbrace;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;8&quot;&gt;&lt;span class=&quot;tag-delimiter&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;text&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;9&quot;&gt;  Download
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;10&quot;&gt;&lt;/span&gt;&lt;span class=&quot;tag-delimiter&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No LiveView round-trip needed — &lt;code&gt;JS.dispatch&lt;/code&gt; fires the event directly in the browser, the &lt;code&gt;window&lt;/code&gt; listener catches it, and the download happens entirely client-side after the one proxy fetch.&lt;/p&gt;
&lt;h1&gt;Why &lt;code&gt;naturalWidth&lt;/code&gt; instead of &lt;code&gt;width&lt;/code&gt;?&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;img.width&lt;/code&gt; returns the CSS-rendered size, which is meaningless for an image that isn&apos;t attached to the DOM. &lt;code&gt;img.naturalWidth&lt;/code&gt; always returns the actual pixel dimensions of the image data — the right value to use when doing pixel-level canvas operations.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/javascript&quot;&gt;#javascript&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/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>
    <published>2026-05-29T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/downloading-external-images-as-squares-from-a-phoenix-app</id>
    <title>🐥 Downloading external images as squares from a Phoenix app</title>
    <updated>2026-05-29T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/programming-in-2026-excitement-dread-and-the-coming-wave" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;A big part of software engineering is now communicating with an alien technology we don&apos;t - and can&apos;t - fully understand.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://amontalenti.com/2026/04/23/excitement-and-dread&quot;&gt;Continue reading on &lt;strong&gt;amontalenti.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>
    <published>2026-05-29T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/programming-in-2026-excitement-dread-and-the-coming-wave</id>
    <title>🔗 Programming in 2026: excitement, dread, and the coming wave</title>
    <updated>2026-05-29T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/when-everyone-has-ai-and-the-company-still-learns-nothing" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Individual AI productivity gains don&apos;t automatically become organizational ones. Companies are past the &quot;buy seats and run training&quot; phase and into a messier stage where adoption is uneven, partially hidden, and disconnected from real learning. The key question isn&apos;t whether people use AI, but whether the organization captures what changed because of it. That requires linking operational control, loop-level feedback, and capability sharing into something that builds learning velocity over time.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://www.robert-glaser.de/when-everyone-has-ai-and-the-company-still-learns-nothing/&quot;&gt;Continue reading on &lt;strong&gt;www.robert-glaser.de&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>
    <published>2026-05-29T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/when-everyone-has-ai-and-the-company-still-learns-nothing</id>
    <title>🔗 When everyone has AI and the company still learns nothing</title>
    <updated>2026-05-29T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/claude-code-is-not-making-your-product-better" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;At the frontier, it&apos;s not clear that spending on tokens produces any economic value at all. The bottleneck at that level is tastemakers. The taste to delete, compress, and refuse is more valuable now that the floor is rising. AI makes it possible for anyone to create generic products, but it won&apos;t help the highest-level artisans create better products.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://ethanding.substack.com/p/claude-code-is-not-making-your-product&quot;&gt;Continue reading on &lt;strong&gt;ethanding.substack.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>
    <published>2026-05-28T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/claude-code-is-not-making-your-product-better</id>
    <title>🔗 Claude code is not making your product better</title>
    <updated>2026-05-28T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/s3-files-and-the-changing-face-of-s3" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;The frustrating process of moving large datasets is all too familiar in many industries. In the article, Andy Warfield recounts his experiences with genomics researchers at UBC, who spent excessive time managing data rather than analyzing it. He introduced &lt;code&gt;S3 Files&lt;/code&gt; to bridge this gap between S3 and local filesystems, simplifying data workflows. The introduction of unified data primitives like &lt;code&gt;S3 Tables&lt;/code&gt; and &lt;code&gt;S3 Vectors&lt;/code&gt; supports structured data and semantic search more effectively. With &lt;code&gt;S3 Files&lt;/code&gt;, any existing S3 data can now be accessed directly as a network-attached filesystem, paving the way for enhanced data interaction across various tools.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://www.allthingsdistributed.com/2026/04/s3-files-and-the-changing-face-of-s3.html&quot;&gt;Continue reading on &lt;strong&gt;www.allthingsdistributed.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/tools&quot;&gt;#tools&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/devops&quot;&gt;#devops&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-05-28T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/s3-files-and-the-changing-face-of-s3</id>
    <title>🔗 S3 Files and the changing face of S3</title>
    <updated>2026-05-28T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/phoenix-liveview-widgets-with-hooks-a-reusable-pattern" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Reusable Phoenix widgets have often been a challenge in Elixir applications, especially when managing state and events without complicating the parent LiveView. This article highlights a unique pattern for creating stateful and interactive components using function components and hooks. The method allows components to be self-contained, managing their own state while seamlessly integrating into the LiveView lifecycle.&lt;/p&gt;
&lt;p&gt;It covers the limitations of LiveComponents and Embedded LiveViews, demonstrating how the pattern can simplify component development by treating the LiveView&apos;s socket as the single source of truth. By implementing hooks, developers intercept events without requiring the parent LiveView to handle internal complexities, enabling multiple instances to run independently in the same context. A practical example involves building a chat widget, detailing the essential steps and considerations to maintain performance and reliability.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://curiosum.com/blog/hooking-up-with-liveview-stateful-widgets-with-function-components&quot;&gt;Continue reading on &lt;strong&gt;curiosum.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/pattern&quot;&gt;#pattern&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>
    <published>2026-05-28T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/phoenix-liveview-widgets-with-hooks-a-reusable-pattern</id>
    <title>🔗 Phoenix LiveView widgets with hooks: a reusable pattern</title>
    <updated>2026-05-28T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/the-tailwind-enabled-selector-trick-for-disabled-buttons" rel="alternate"/>
    <content type="html">&lt;p&gt;When you add a &lt;code&gt;disabled&lt;/code&gt; attribute to a &lt;code&gt;&lt;button&gt;&lt;/code&gt; element, you probably expect it to just... look disabled. But there&apos;s a subtle trap that catches a lot of developers: your &lt;code&gt;hover:&lt;/code&gt; styles still apply visually, even when the button is disabled.&lt;/p&gt;
&lt;h1&gt;The problem&lt;/h1&gt;
&lt;p&gt;Consider a typical Tailwind button:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-html&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;bg-gray-200 hover:bg-gray-300 hover:cursor-pointer&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;disabled&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;text&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;2&quot;&gt;  Click me
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;3&quot;&gt;&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even though the button is &lt;code&gt;disabled&lt;/code&gt; and won&apos;t fire any events, the &lt;code&gt;hover:bg-gray-300&lt;/code&gt; and &lt;code&gt;hover:cursor-pointer&lt;/code&gt; classes still apply on hover. The cursor becomes a pointer and the background changes — giving the user a false signal that the button is interactive.&lt;/p&gt;
&lt;h1&gt;The fix: &lt;code&gt;enabled:&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;Tailwind ships with an &lt;code&gt;enabled:&lt;/code&gt; variant that maps directly to the CSS &lt;code&gt;:enabled&lt;/code&gt; pseudo-class. Swap your &lt;code&gt;hover:&lt;/code&gt; styles for &lt;code&gt;enabled:hover:&lt;/code&gt; and the problem disappears:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-html&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;bg-gray-200 enabled:hover:bg-gray-300 enabled:hover:cursor-pointer&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;disabled&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;text&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;2&quot;&gt;  Click me
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;3&quot;&gt;&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now those styles only apply when the button is &lt;strong&gt;not&lt;/strong&gt; disabled. No JavaScript, no conditional class logic, no extra wrapper — just a single variant prefix.&lt;/p&gt;
&lt;h1&gt;Why this matters&lt;/h1&gt;
&lt;p&gt;The &lt;code&gt;:enabled&lt;/code&gt; pseudo-class is the semantic opposite of &lt;code&gt;:disabled&lt;/code&gt;. It&apos;s supported in all modern browsers and has been in CSS for years, but it&apos;s easy to overlook because disabled states are often handled with opacity or a wrapper &lt;code&gt;div&lt;/code&gt; instead.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;enabled:hover:&lt;/code&gt; keeps your intent explicit in the markup and makes disabled state handling a one-liner in your component library.&lt;/p&gt;
&lt;h1&gt;In practice (Phoenix / LiveView)&lt;/h1&gt;
&lt;p&gt;If you have a reusable button component, this is the ideal place to apply it. Instead of:&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;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;string&quot;&gt;&quot;hover:bg-gray-300 hover:cursor-pointer&quot;&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Write:&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;div class=&quot;line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;string&quot;&gt;&quot;enabled:hover:bg-gray-300 enabled:hover:cursor-pointer&quot;&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now any caller that passes &lt;code&gt;disabled&lt;/code&gt; as an attribute gets correct visual behavior automatically — no special-case classes needed at the call site.&lt;/p&gt;
&lt;p&gt;Small trick, but it saves a prop, a conditional, and a subtle UX bug all at once.&lt;/p&gt;&lt;p&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/best-practice&quot;&gt;#best-practice&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-05-27T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/the-tailwind-enabled-selector-trick-for-disabled-buttons</id>
    <title>🐥 The Tailwind `enabled:` selector trick for disabled buttons</title>
    <updated>2026-05-27T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/recursion-as-a-design-pattern" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;What if recursion was the best way to handle list processing in Elixir? This article reveals how recursion isn&apos;t just an alternative to loops but the optimal method in Elixir for list manipulation. Developers often struggle with traditional loop constructs, but Elixir’s approach leverages the accumulator pattern to streamline operations. The article contrasts two recursive styles for summing lists, illustrating how using an accumulator enhances performance by eliminating waiting stacks.&lt;/p&gt;
&lt;p&gt;By understanding this pattern, users can resolve confusion over recursion and recognize it as a natural fit for Elixir&apos;s functional paradigm. Concepts like &lt;code&gt;Enum.reduce&lt;/code&gt; align with the accumulator, making it clearer how to harness recursion efficiently in real-world scenarios. Bruce Tate’s insights underscore the necessity for shifting perspectives about recursion to see it as an inherent feature rather than a workaround.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://grox.io/blog/26-recursion-as-a-design-pattern/&quot;&gt;Continue reading on &lt;strong&gt;grox.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/pattern&quot;&gt;#pattern&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>
    <published>2026-05-27T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/recursion-as-a-design-pattern</id>
    <title>🔗 Recursion as a design pattern</title>
    <updated>2026-05-27T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/dont-let-ai-write-for-you" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Do LLMs enhance or hinder writing? This provocative piece warns against the pitfalls of using AI to generate documents and essays. The author argues that writing is not just about producing text; it’s an opportunity to explore ideas and build understanding. By relying on LLMs, individuals risk undermining their own credibility and authentic thinking. While LLMs can serve functional roles, like idea generation or quick transcription, they should not replace the deep cognitive process associated with writing. The article emphasizes the importance of engaging with the content and reflects on how LLM-generated writing impacts personal trustworthiness. To truly benefit from LLM tools, users must elevate their own thoughtfulness in the writing process.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://alexhwoods.com/dont-let-ai-write-for-you/&quot;&gt;Continue reading on &lt;strong&gt;alexhwoods.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/ai&quot;&gt;#ai&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-05-27T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/dont-let-ai-write-for-you</id>
    <title>🔗 Don&apos;t let AI write for you</title>
    <updated>2026-05-27T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/the-pulse-token-spend-breaks-budgets-what-next" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;In the past couple of months, spending on AI agents has surged dramatically among tech companies, with many experiencing a nearly 10x increase in costs. Gergely Orosz highlights the trend of &quot;tokenmaxxing,&quot; where developers are incentivized to maximize their AI usage for personal standings on internal leaderboards, raising concerns for leadership about the sustainability of such spending. Insights from 15 businesses showcase that while some companies are currently seeing productivity increase, their token costs are becoming unsustainable. Companies are now considering strategies ranging from limiting model usage to encouraging more efficient token consumption. The article illustrates how firms are navigating this financial pressure amidst a growing embrace of AI in their operations and the shifting perceptions around its costs.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://newsletter.pragmaticengineer.com/p/the-pulse-token-spend-breaks-budgets?isFreemail=true&amp;post_id=196007666&amp;publication_id=458709&amp;r=a3w8x&amp;triedRedirect=true&quot;&gt;Continue reading on &lt;strong&gt;newsletter.pragmaticengineer.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/ai&quot;&gt;#ai&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/announcement&quot;&gt;#announcement&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-05-26T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/the-pulse-token-spend-breaks-budgets-what-next</id>
    <title>🔗 The Pulse: token spend breaks budgets – what next?</title>
    <updated>2026-05-26T17:00:00Z</updated>
  </entry>
</feed>