<?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 rel="alternate" href="https://www.yellowduck.be"/>
  <link rel="self" href="https://www.yellowduck.be/posts/feed"/>
  <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-03-11T14:00:00Z</updated>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/100-percent-code-coverage-is-not-as-good-as-you-think"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;When I see people talk about code coverage, there are usually caveats that you don&apos;t need to worry about 100% coverage. Some argue that it ends up being a victim of Goodhart&apos;s Law1, others say that you get diminishing returns. Some will say that full code coverage is just a by-product of the proper development process, which should be your focus. Others will point out that some things just don&apos;t need test coverage. &quot;Be pragmatic!&quot;, they&apos;ll say.&lt;/p&gt;
&lt;p&gt;But lately, since AI agents have become more common, I do see people starting to shoot for that perfect 100%. Some feel that since they can just throw AI at it, they might as well ask for it. Others think of it as a hedge against the unexpected things their agent might do, that it&apos;s just the kind of guardrails you need in this kind of workflow.&lt;/p&gt;
&lt;p&gt;There&apos;s a fundamental point that&apos;s missed here.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://blog.1-800-rad-dude.com/posts/2026/02-15-100-Percent-Code-Coverage.html&quot;&gt;Continue reading on &lt;strong&gt;blog.1-800-rad-dude.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/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-03-11T14:00:00Z</published>
    <id>https://www.yellowduck.be/posts/100-percent-code-coverage-is-not-as-good-as-you-think</id>
    <title>🔗 100 percent code coverage is not as good as you think</title>
    <updated>2026-03-11T14:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/getting-started-with-typst"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Typst is an open-source project that can be built into a pretty small binary, that is very easy to install or even use in Web. Typst is managed and built by a company, a company, the main product of which is Typst editing Web App.&lt;/p&gt;
&lt;p&gt;So there are two possibilities for working with Typst: use online Web App or install Typst locally and work with it from your favorite editor. I will briefly cover these two ways with their pros and cons in this chapter.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://sitandr.github.io/typst-examples-book/book/getting_started.html&quot;&gt;Continue reading on &lt;strong&gt;sitandr.github.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;/p&gt;</content>
    <published>2026-03-11T09:00:00Z</published>
    <id>https://www.yellowduck.be/posts/getting-started-with-typst</id>
    <title>🔗 Getting started with Typst</title>
    <updated>2026-03-11T09:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/thinking-elixir-podcast-295-is-your-type-system-leaking"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;News includes José Valim’s deep dive into Elixir’s type system cutting worst-case checks from 10s to 25ms, a new Dashbit post on type systems as leaky abstractions, Oban Pro teasing Workflow UI improvements, MDEx v0.11.6, Livebook Desktop on Linux, and more!&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://podcast.thinkingelixir.com/295&quot;&gt;Continue reading on &lt;strong&gt;podcast.thinkingelixir.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/elixir&quot;&gt;#elixir&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/linux&quot;&gt;#linux&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-03-10T18:00:00Z</published>
    <id>https://www.yellowduck.be/posts/thinking-elixir-podcast-295-is-your-type-system-leaking</id>
    <title>🔗 Thinking Elixir podcast 295: Is Your Type System Leaking?</title>
    <updated>2026-03-10T18:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/enum-ordering-in-postgresql"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Rather than adhering to alphabetical order, when using &lt;code&gt;order by&lt;/code&gt; with an enum column, the sorting adheres to the order in which the values were listed when the enum was created.&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-sql&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;create&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;application_status&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;type-builtin&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;#39;submitted&amp;#39;&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;#39;under_review&amp;#39;&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;#39;accepted&amp;#39;&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;#39;rejected&amp;#39;&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;2&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;applications&lt;/span&gt; &lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;variable-member&quot;&gt;applicant_name&lt;/span&gt; &lt;span class=&quot;type-builtin&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;variable-member&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;application_status&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;keyword&quot;&gt;insert&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;into&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;applications&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;values&lt;/span&gt; &lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;#39;George Washington&amp;#39;&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;#39;accepted&amp;#39;&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;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;#39;John Adams&amp;#39;&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;#39;under_review&amp;#39;&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;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;#39;Thomas Jefferson&amp;#39;&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;#39;under_review&amp;#39;&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;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;#39;Aaron Burr&amp;#39;&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;#39;rejected&amp;#39;&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;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;#39;Thomas Pinckney&amp;#39;&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;#39;submitted&amp;#39;&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;4&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;applications&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;keyword-operator&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;variable-member&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;-- applicant_name | status -- ------------------+------------- -- Thomas Pinckney | submitted -- John Adams | under_review -- Thomas Jefferson | under_review -- George Washington | accepted -- Aaron Burr | rejected&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://til.hashrocket.com/posts/tpvvhmnoez-enum-ordering-in-postgres&quot;&gt;Continue reading on &lt;strong&gt;til.hashrocket.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/database&quot;&gt;#database&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/postgresql&quot;&gt;#postgresql&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/sql&quot;&gt;#sql&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-03-10T14:00:00Z</published>
    <id>https://www.yellowduck.be/posts/enum-ordering-in-postgresql</id>
    <title>🔗 Enum ordering in PostgreSQL</title>
    <updated>2026-03-10T14:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/hidden-issue-with-wherefulltext-and-refresh-database-in-laravel"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;whereFulltext&lt;/code&gt; method in Laravel allows developers to perform full-text searches on columns indexed with &lt;code&gt;FULLTEXT&lt;/code&gt; in MySQL and PostgreSQL (&lt;a href=&quot;https://laravel.com/docs/11.x/queries#full-text-where-clauses&quot;&gt;docs&lt;/a&gt;). This method is useful for searching large datasets efficiently, especially for text-based searches. However, while it enhances search performance, it has some caveats, particularly when used in tests with &lt;code&gt;RefreshDatabase&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://dev.to/tegos/hidden-issue-with-wherefulltext-and-refreshdatabase-in-laravel-2p4f&quot;&gt;Continue reading on &lt;strong&gt;dev.to&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/laravel&quot;&gt;#laravel&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/php&quot;&gt;#php&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/database&quot;&gt;#database&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/postgresql&quot;&gt;#postgresql&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/mysql&quot;&gt;#mysql&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/sql&quot;&gt;#sql&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-03-10T09:00:00Z</published>
    <id>https://www.yellowduck.be/posts/hidden-issue-with-wherefulltext-and-refresh-database-in-laravel</id>
    <title>🔗 Hidden issue with whereFullText and refresh database in Laravel</title>
    <updated>2026-03-10T09:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/pattern-matching-matching-not-assigning-groxio-blog"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;You come to Elixir with an assignment model in your head. You see = and your hands move faster than your brain. Then a match fails, or a function head doesn’t run, and you think you made a mistake.&lt;/p&gt;
&lt;p&gt;Pattern matching is not about storing a value. It’s about selecting a shape. When the shape doesn’t fit, the match fails, and that failure is useful. It’s how Elixir chooses which path to take.&lt;/p&gt;
&lt;p&gt;The mental model is simple: = is a match operator. Pattern matching selects structure and binds names. The pin operator turns a binding into a test.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://grox.io/blog/16-pattern-matching-matching-not-assigning/&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/elixir&quot;&gt;#elixir&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-03-09T18:00:00Z</published>
    <id>https://www.yellowduck.be/posts/pattern-matching-matching-not-assigning-groxio-blog</id>
    <title>🔗 Pattern matching: matching, not assigning</title>
    <updated>2026-03-09T18:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/laravel-boost-ai-tooling-for-laravel-by-the-laravel-team"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Laravel Boost is designed to enhance your development experience by providing powerful tools and integration capabilities. With the ability to work seamlessly within your editor and alongside Laravel applications, it aims to streamline the development process and improve productivity.&lt;/p&gt;
&lt;p&gt;Many developers seek ways to accelerate their workflow, and Laravel Boost offers solutions that can help achieve those goals efficiently. By leveraging the features of this tool, you can focus more on building exceptional applications while reducing the overhead of repetitive tasks.&lt;/p&gt;
&lt;p&gt;Key Features&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deep integration with popular code editors.&lt;/li&gt;
&lt;li&gt;Tools that assist in the Laravel development cycle.&lt;/li&gt;
&lt;li&gt;Accelerated build pipelines and deployment processes.&lt;/li&gt;
&lt;li&gt;Enhanced debugging capabilities.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://laravel.com/ai/boost&quot;&gt;Continue reading on &lt;strong&gt;laravel.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/laravel&quot;&gt;#laravel&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/php&quot;&gt;#php&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/tools&quot;&gt;#tools&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-03-09T14:00:00Z</published>
    <id>https://www.yellowduck.be/posts/laravel-boost-ai-tooling-for-laravel-by-the-laravel-team</id>
    <title>🔗 Laravel Boost – AI tooling for Laravel</title>
    <updated>2026-03-09T14:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/cross-site-request-forgery"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/CSRF&quot;&gt;Cross-Site Request Forgery (CSRF)&lt;/a&gt; is a &lt;a href=&quot;https://en.wikipedia.org/wiki/Confused_deputy_problem&quot;&gt;confused deputy&lt;/a&gt; attack where the attacker causes the browser to send a request to a target using the ambient authority of the user’s cookies or network position.&lt;sup id=&quot;fnref:pna&quot;&gt;&lt;a class=&quot;footnote-ref&quot; href=&quot;https://www.yellowduck.be#fn:pna&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; For example, &lt;code&gt;attacker.example&lt;/code&gt; can serve the following HTML to a victim&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;form action=&quot;https://example.com/send-money&quot; method=&quot;post&quot;&gt; &lt;input type=&quot;hidden&quot; name=&quot;to&quot; value=&quot;filippo&quot; /&gt; &lt;input type=&quot;hidden&quot; name=&quot;amount&quot; value=&quot;1000000&quot; /&gt; &lt;/form&gt; &lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and the browser will send a POST request to &lt;code&gt;https://example.com/send-money&lt;/code&gt; using the victim’s cookies.&lt;/p&gt;
&lt;p&gt;Essentially all applications that use cookies for authentication need to protect against CSRF. Importantly, this is not about protecting against an attacker that can make arbitrary requests&lt;sup id=&quot;fnref:api&quot;&gt;&lt;a class=&quot;footnote-ref&quot; href=&quot;https://www.yellowduck.be#fn:api&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; (as an attacker doesn’t know the user’s cookies), but about working with browsers to identify authenticated requests initiated from untrusted sources.&lt;/p&gt;
&lt;p&gt;Unlike &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS&quot;&gt;Cross-Origin Resource Sharing (CORS)&lt;/a&gt;, which is about &lt;em&gt;sharing responses&lt;/em&gt; across origins, CSRF is about accepting state-changing requests, even if the attacker will not see the response. Defending against &lt;a href=&quot;https://xsleaks.dev/&quot;&gt;leaks&lt;/a&gt; is significantly &lt;a href=&quot;https://frederikbraun.de/modern-solutions-xsleaks.html&quot;&gt;more complex and nuanced&lt;/a&gt;, especially in the age of Spectre.&lt;/p&gt;
&lt;p&gt;Why do browsers allow these requests in the first place? Like anything in the Web platform, primarily for legacy reasons: that’s how it used to work and changing it breaks things. Importantly, disabling these &lt;em&gt;third-party cookies&lt;/em&gt; breaks important Single-Sign On (SSO) flows. All CSRF solutions need to support a bypass mechanism for those rare exceptions. (There are also complex intersections with cross-site tracking and privacy concerns, which are beyond the scope of this article.)&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://words.filippo.io/csrf/&quot;&gt;Continue reading on &lt;strong&gt;words.filippo.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/http&quot;&gt;#http&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/html&quot;&gt;#html&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-03-09T09:00:00Z</published>
    <id>https://www.yellowduck.be/posts/cross-site-request-forgery</id>
    <title>🔗 Cross-site request forgery</title>
    <updated>2026-03-09T09:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/monitoring-the-progress-of-creating-an-index-in-postgresql"/>
    <content type="html">&lt;p&gt;Creating an index on a large table can take minutes or even hours. For GIN, trigram, or multi-million row tables, it can feel like nothing is happening at all.&lt;/p&gt;
&lt;p&gt;PostgreSQL provides built-in visibility into index creation progress. You don’t need external tools — just the right system view.&lt;/p&gt;
&lt;h1&gt;The &lt;code&gt;pg_stat_progress_create_index&lt;/code&gt; view&lt;/h1&gt;
&lt;p&gt;PostgreSQL exposes index build progress via:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The key view is:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-sql&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;SELECT&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;2&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;pg_stat_progress_create_index&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;This shows all currently running &lt;code&gt;CREATE INDEX&lt;/code&gt; operations.&lt;/p&gt;
&lt;p&gt;If nothing is building, it returns zero rows.&lt;/p&gt;
&lt;h1&gt;Example output explained&lt;/h1&gt;
&lt;p&gt;A typical query looks like this:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-sql&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;SELECT&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;2&quot;&gt;    &lt;span class=&quot;variable-member&quot;&gt;pid&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;variable-member&quot;&gt;datname&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;span class=&quot;variable-member&quot;&gt;relid&lt;/span&gt;&lt;span class=&quot;text&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;regclass&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;table_name&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;variable-member&quot;&gt;index_relid&lt;/span&gt;&lt;span class=&quot;text&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;regclass&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;index_name&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-member&quot;&gt;phase&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;7&quot;&gt;    &lt;span class=&quot;variable-member&quot;&gt;lockers_total&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;variable-member&quot;&gt;lockers_done&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-member&quot;&gt;blocks_total&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-member&quot;&gt;blocks_done&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;span class=&quot;variable-member&quot;&gt;tuples_total&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;12&quot;&gt;    &lt;span class=&quot;variable-member&quot;&gt;tuples_done&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;FROM&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;pg_stat_progress_create_index&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;Important columns:&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;phase&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Tells you what stage the build is in. Examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;initializing&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;building index&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;waiting for writers before validation&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;index validation&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;waiting for old snapshots&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&apos;re using &lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt;, you’ll see additional validation phases.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;blocks_total&lt;/code&gt; and &lt;code&gt;blocks_done&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;These indicate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Total table blocks to scan&lt;/li&gt;
&lt;li&gt;How many have been processed&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Progress percentage:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-sql&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;/span&gt;&lt;span class=&quot;text&quot;&gt;blocks_done::&lt;/span&gt;&lt;span class=&quot;type-builtin&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;text&quot;&gt; NULLIF&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;text&quot;&gt;blocks_total&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;text&quot;&gt; 0&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;*&lt;/span&gt;&lt;span class=&quot;text&quot;&gt; 100
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is usually the most reliable indicator during the scan phase.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;tuples_total&lt;/code&gt; and &lt;code&gt;tuples_done&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Shows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Estimated total rows&lt;/li&gt;
&lt;li&gt;Rows processed so far&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Useful, but block progress is often more stable.&lt;/p&gt;
&lt;h1&gt;Monitoring concurrent index builds&lt;/h1&gt;
&lt;p&gt;When using:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-sql&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;CREATE&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;CONCURRENTLY&lt;/span&gt; &lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&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;PostgreSQL performs multiple scans and validation steps.&lt;/p&gt;
&lt;p&gt;You’ll see phases like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;building index&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;waiting for writers before validation&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;index validation&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;waiting for old snapshots&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If it appears stuck in:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-sql&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;text&quot;&gt;waiting &lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;old&lt;/span&gt;&lt;span class=&quot;text&quot;&gt; snapshots
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That usually means a long-running transaction is preventing completion.&lt;/p&gt;
&lt;p&gt;You can inspect active transactions:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-sql&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;SELECT&lt;/span&gt; &lt;span class=&quot;variable-member&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;variable-member&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;variable-member&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;variable-member&quot;&gt;xact_start&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;FROM&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;pg_stat_activity&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;WHERE&lt;/span&gt; &lt;span class=&quot;variable-member&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;#39;idle&amp;#39;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;4&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;keyword-operator&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;variable-member&quot;&gt;xact_start&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Calculating progress percentage&lt;/h1&gt;
&lt;p&gt;A simple progress query:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-sql&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;SELECT&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;2&quot;&gt;    &lt;span class=&quot;variable-member&quot;&gt;relid&lt;/span&gt;&lt;span class=&quot;text&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;regclass&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;table_name&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;variable-member&quot;&gt;index_relid&lt;/span&gt;&lt;span class=&quot;text&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;regclass&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;index_name&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;span class=&quot;variable-member&quot;&gt;phase&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;type&quot;&gt;ROUND&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;6&quot;&gt;        &lt;span class=&quot;string&quot;&gt;100.0&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;variable-member&quot;&gt;blocks_done&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;NULLIF&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable-member&quot;&gt;blocks_total&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number-float&quot;&gt;0&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;7&quot;&gt;        &lt;span class=&quot;number-float&quot;&gt;2&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;8&quot;&gt;    &lt;span class=&quot;punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;progress_percent&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;9&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;pg_stat_progress_create_index&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;This gives you a clean percentage during the main build phase.&lt;/p&gt;
&lt;h1&gt;When progress appears frozen&lt;/h1&gt;
&lt;p&gt;Common reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Very small &lt;code&gt;maintenance_work_mem&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Disk I/O saturation&lt;/li&gt;
&lt;li&gt;WAL bottlenecks&lt;/li&gt;
&lt;li&gt;Waiting on long-running transactions (concurrent builds)&lt;/li&gt;
&lt;li&gt;CPU-heavy index types (e.g. GIN with trigrams)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If blocks aren’t increasing, check:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-sql&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;SELECT&lt;/span&gt; &lt;span class=&quot;variable-member&quot;&gt;wait_event_type&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;variable-member&quot;&gt;wait_event&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;FROM&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;pg_stat_activity&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;WHERE&lt;/span&gt; &lt;span class=&quot;variable-member&quot;&gt;pid&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;variable-member&quot;&gt;index_pid&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&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;This tells you whether it’s waiting on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lock&lt;/li&gt;
&lt;li&gt;IO&lt;/li&gt;
&lt;li&gt;WAL&lt;/li&gt;
&lt;li&gt;Buffer pin&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Estimating total duration&lt;/h1&gt;
&lt;p&gt;A rough estimate during build:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-sql&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;SELECT&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;2&quot;&gt;    &lt;span class=&quot;variable-member&quot;&gt;blocks_done&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;variable-member&quot;&gt;blocks_total&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;span class=&quot;type&quot;&gt;now&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;-&lt;/span&gt; &lt;span class=&quot;variable-member&quot;&gt;backend_start&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;elapsed&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;5&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;pg_stat_progress_create_index&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;6&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;pg_stat_activity&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;USING&lt;/span&gt; &lt;span class=&quot;punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;text&quot;&gt;pid&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;You can extrapolate remaining time from block progress.&lt;/p&gt;
&lt;h1&gt;Version requirement&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;pg_stat_progress_create_index&lt;/code&gt; is available since:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PostgreSQL 12&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you’re on an older version, you won’t have native progress tracking.&lt;/p&gt;
&lt;h1&gt;Practical workflow&lt;/h1&gt;
&lt;p&gt;When building a large index in production:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;CREATE INDEX&lt;/code&gt; (or &lt;code&gt;CONCURRENTLY&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Monitor &lt;code&gt;pg_stat_progress_create_index&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Watch for blocking transactions&lt;/li&gt;
&lt;li&gt;Track I/O saturation&lt;/li&gt;
&lt;li&gt;Increase &lt;code&gt;maintenance_work_mem&lt;/code&gt; if needed&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This gives you visibility instead of guessing.&lt;/p&gt;
&lt;h1&gt;Final thoughts&lt;/h1&gt;
&lt;p&gt;Large index builds are expensive operations. Monitoring progress:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reduces uncertainty&lt;/li&gt;
&lt;li&gt;Helps detect blockers&lt;/li&gt;
&lt;li&gt;Allows time estimation&lt;/li&gt;
&lt;li&gt;Makes concurrent builds safer in production&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you regularly build large GIN or trigram indexes, having a monitoring query ready in your toolbox saves a lot of stress.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/database&quot;&gt;#database&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/tools&quot;&gt;#tools&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/postgresql&quot;&gt;#postgresql&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/sql&quot;&gt;#sql&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-03-08T18:00:00Z</published>
    <id>https://www.yellowduck.be/posts/monitoring-the-progress-of-creating-an-index-in-postgresql</id>
    <title>🐥 Monitoring the progress of creating an index in PostgreSQL</title>
    <updated>2026-03-08T18:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/my-ai-adoption-journey"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;My experience adopting any meaningful tool is that I&apos;ve necessarily gone through three phases: (1) a period of inefficiency (2) a period of adequacy, then finally (3) a period of workflow and life-altering discovery.&lt;/p&gt;
&lt;p&gt;In most cases, I have to force myself through phase 1 and 2 because I usually have a workflow I&apos;m already happy and comfortable with. Adopting a tool feels like work, and I &lt;em&gt;do not&lt;/em&gt; want to put in the effort, but I usually do in an effort to be a well-rounded person of my craft.&lt;/p&gt;
&lt;p&gt;This is my journey of how I found value in AI tooling and what I&apos;m trying next with it. In an ocean of overly dramatic, hyped takes, I hope this represents a more nuanced, measured approach to my views on AI and how they&apos;ve changed over time.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This blog post was fully written by hand, in my own words. I hate that I have to say that but especially given the subject matter, I want to be explicit about it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://mitchellh.com/writing/my-ai-adoption-journey&quot;&gt;Continue reading on &lt;strong&gt;mitchellh.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/ai&quot;&gt;#ai&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-03-08T14:00:00Z</published>
    <id>https://www.yellowduck.be/posts/my-ai-adoption-journey</id>
    <title>🔗 My AI adoption journey</title>
    <updated>2026-03-08T14:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/on-cognitive-debt"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;People are talking about &lt;em&gt;cognitive debt&lt;/em&gt;, the idea that AI-generated codebases are at risk of falling into a state where nobody knows how they work. This threatens to leave them inextensible, unobservable, and hard to debug.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.natemeyvis.com/on-cognitive-debt/https://simonwillison.net/2026/Feb/15/cognitive-debt/&quot;&gt;Here&apos;s Simon Willison&lt;/a&gt; citing &lt;a href=&quot;https://www.natemeyvis.com/on-cognitive-debt/https://margaretstorey.com/blog/2026/02/09/cognitive-debt/&quot;&gt;Margaret-Anne Storey&lt;/a&gt;, as discussed &lt;a href=&quot;https://www.natemeyvis.com/on-cognitive-debt/https://martinfowler.com/fragments/2026-02-13.html&quot;&gt;by Martin Fowler&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&quot;Cognitive debt&quot; is a lovely term for this: it describes an important phenomenon that is much to be avoided. It is absolutely happening with AI-driven codebases--the links above have examples, and I&apos;ve felt it in my own projects.&lt;/p&gt;
&lt;p&gt;I suspect, however, that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;When you control for the size and scope of the project, cognitive debt tends to be at least as bad, and usually worse, in pre-AI codebases than in AI codebases;&lt;/li&gt;
&lt;li&gt;This fact is obscured because so much of what is normalized as traditional engineering work is in fact either managing crippling cognitive debt or avoiding it at enormous cost;&lt;/li&gt;
&lt;li&gt;The best users of AI are already pretty good at avoiding cognitive debt, and we’re only going to get better at it.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://www.natemeyvis.com/on-cognitive-debt/&quot;&gt;Continue reading on &lt;strong&gt;www.natemeyvis.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/ai&quot;&gt;#ai&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-03-08T09:00:00Z</published>
    <id>https://www.yellowduck.be/posts/on-cognitive-debt</id>
    <title>🔗 On cognitive debt</title>
    <updated>2026-03-08T09:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/upgrading-amignosis-phoenix-and-elixir-with-claude-code"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;My company, Amignosis, &lt;a href=&quot;https://amignosis.com/&quot;&gt;website&lt;/a&gt; is a simple Phoenix LiveView web app. It was on Phoenix 1.7.21 and Elixir 1.18, and I wanted to upgrade it to Phoenix 1.8.3 and Elixir 1.19. So, I decided to try &lt;a href=&quot;https://tidewave.ai/&quot;&gt;Tidewave&lt;/a&gt; with Claude Code, to do it for me.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://petros.blog/2026/02/15/upgrading-an-elixir-phoenix-app-using-tidewave/&quot;&gt;Continue reading on &lt;strong&gt;petros.blog&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/ai&quot;&gt;#ai&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-03-07T18:00:00Z</published>
    <id>https://www.yellowduck.be/posts/upgrading-amignosis-phoenix-and-elixir-with-claude-code</id>
    <title>🔗 Upgrading Amignosis: Phoenix and Elixir with Claude Code</title>
    <updated>2026-03-07T18:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/how-to-run-a-technical-due-diligence"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Chances are that in your position as tech leader, you’ve performed technical due diligence on another company.&lt;/p&gt;
&lt;p&gt;Or maybe you’ve been on the receiving end, having to answer enquiries from consultants or fellow tech leaders.&lt;/p&gt;
&lt;p&gt;Lastly, you might be among those who have never taken part in such a process ever, and you might even wonder what the hell technical due diligence is.&lt;/p&gt;
&lt;p&gt;If you’ve always been afraid to ask for fear of looking dumb, you have two options. Linger in your secret ignorance, hoping nobody will find out, or keep reading.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://makemeacto.substack.com/p/how-to-run-a-technical-due-diligence&quot;&gt;Continue reading on &lt;strong&gt;makemeacto.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;/p&gt;</content>
    <published>2026-03-07T14:00:00Z</published>
    <id>https://www.yellowduck.be/posts/how-to-run-a-technical-due-diligence</id>
    <title>🔗 How to run a technical due diligence?</title>
    <updated>2026-03-07T14:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/fair-queue-distribution-with-laravels-balanced-queue"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Laravel Balanced Queue, created by &lt;a href=&quot;https://github.com/YanGusik&quot;&gt;YanGusik&lt;/a&gt;, manages queue distribution across user groups with per-user concurrency control and multiple partition strategies to prevent queue monopolization.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://laravel-news.com/fair-queue-distribution-with-laravel-balanced-queue&quot;&gt;Continue reading on &lt;strong&gt;laravel-news.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/laravel&quot;&gt;#laravel&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/php&quot;&gt;#php&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-03-07T09:00:00Z</published>
    <id>https://www.yellowduck.be/posts/fair-queue-distribution-with-laravels-balanced-queue</id>
    <title>🔗 Fair queue distribution with Laravel&apos;s balanced queue</title>
    <updated>2026-03-07T09:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/everything-i-was-lied-to-about-node-js-came-true-with-elixir"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;I&apos;ve been a &lt;a href=&quot;https://nodejs.org/en&quot;&gt;NodeJS&lt;/a&gt; developer for a few years now. I loved the simplicity of the language and the community around it. The fact that I could re-use libraries between my frontend and backend was magic. But as my applications grew in complexity, I started to realise much of what I was told about NodeJS was a lie.&lt;/p&gt;
&lt;p&gt;Recently I started investigating the &lt;a href=&quot;https://elixir-lang.org/&quot;&gt;Elixir&lt;/a&gt;, &lt;a href=&quot;https://www.erlang.org/&quot;&gt;Erlang&lt;/a&gt;, &lt;a href=&quot;https://gleam.run/&quot;&gt;Gleam&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/BEAM_(Erlang_virtual_machine)&quot;&gt;BEAM&lt;/a&gt; ecosystem. I was impressed by the simplicity and power of the runtime. I realised that much of what people say about NodeJS is actually true about Elixir.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://dylans.link/blog/2025-03-03-everything-i-was-lied-to-about-nodejs-came-true-with-elixir&quot;&gt;Continue reading on &lt;strong&gt;dylans.link&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/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;/p&gt;</content>
    <published>2026-03-06T18:00:00Z</published>
    <id>https://www.yellowduck.be/posts/everything-i-was-lied-to-about-node-js-came-true-with-elixir</id>
    <title>🔗 Everything I was lied to about Node.js came true with Elixir</title>
    <updated>2026-03-06T18:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/go-feature-modernized-go-fix"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;go fix&lt;/code&gt; is re-implemented using the Go &lt;a href=&quot;https://pkg.go.dev/golang.org/x/tools/go/analysis&quot;&gt;analysis framework&lt;/a&gt; — the same one &lt;code&gt;go vet&lt;/code&gt; uses.&lt;/p&gt;
&lt;p&gt;While &lt;code&gt;go fix&lt;/code&gt; and &lt;code&gt;go vet&lt;/code&gt; now use the same infrastructure, they have different purposes and use different sets of analyzers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vet is for reporting problems. Its analyzers describe actual issues, but they don&apos;t always suggest fixes, and the fixes aren&apos;t always safe to apply.&lt;/li&gt;
&lt;li&gt;Fix is (mostly) for modernizing the code to use newer language and library features. Its analyzers produce fixes are always safe to apply, but don&apos;t necessarily indicate problems with the code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See the full set of fix&apos;s analyzers in the &lt;em&gt;Analyzers&lt;/em&gt; section.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://antonz.org/accepted/modernized-go-fix/&quot;&gt;Continue reading on &lt;strong&gt;antonz.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/golang&quot;&gt;#golang&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/tools&quot;&gt;#tools&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-03-06T14:00:00Z</published>
    <id>https://www.yellowduck.be/posts/go-feature-modernized-go-fix</id>
    <title>🔗 Go feature: Modernized Go fix</title>
    <updated>2026-03-06T14:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/things-i-check-before-opening-a-pr"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;What is a programmer but a series of PRs (pull requests)? I optimize PRs to introduce the best code I can, be easy to review, and document my work so I can make sense of it in the future. Here are some things I always check before opening a PR.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://www.jakeworth.com/posts/things-i-check-before-opening-a-pr/&quot;&gt;Continue reading on &lt;strong&gt;www.jakeworth.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-03-06T09:00:00Z</published>
    <id>https://www.yellowduck.be/posts/things-i-check-before-opening-a-pr</id>
    <title>🔗 Things I check before opening a PR</title>
    <updated>2026-03-06T09:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/why-prefer-const-arrow-functions-over-function-declarations-in-typescript"/>
    <content type="html">&lt;p&gt;In modern TypeScript codebases, you’ll often see functions defined like this:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-typescript&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;function&quot;&gt;doSomething&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;punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type-builtin&quot;&gt;void&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;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;/code&gt;&lt;/pre&gt;
&lt;p&gt;instead of:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-typescript&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;doSomething&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;type-builtin&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;punctuation-bracket&quot;&gt;&amp;lbrace;&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;&amp;rbrace;&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both are valid. But there are good reasons why many teams prefer the &lt;code&gt;const&lt;/code&gt; + arrow function style as a default.&lt;/p&gt;
&lt;h2&gt;Predictable execution order&lt;/h2&gt;
&lt;p&gt;Function declarations are hoisted:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-typescript&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-call&quot;&gt;doSomething&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;comment&quot;&gt;// Works&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;keyword-function&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;function&quot;&gt;doSomething&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;type-builtin&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;punctuation-bracket&quot;&gt;&amp;lbrace;&lt;/span&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;&amp;rbrace;&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This can hide ordering problems and make large modules harder to reason about.&lt;/p&gt;
&lt;p&gt;Arrow functions assigned to &lt;code&gt;const&lt;/code&gt; are not callable before initialization:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-typescript&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-call&quot;&gt;doSomething&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;comment&quot;&gt;// ReferenceError&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;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;function&quot;&gt;doSomething&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;punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type-builtin&quot;&gt;void&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;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;/code&gt;&lt;/pre&gt;
&lt;p&gt;This makes execution order explicit and avoids subtle refactoring issues.&lt;/p&gt;
&lt;h1&gt;Immutability by default&lt;/h1&gt;
&lt;p&gt;Using &lt;code&gt;const&lt;/code&gt; makes it clear the function reference should not change:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-typescript&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;function&quot;&gt;doSomething&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;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;/code&gt;&lt;/pre&gt;
&lt;p&gt;Treating functions as immutable values aligns well with modern TypeScript practices and reduces accidental reassignment.&lt;/p&gt;
&lt;h1&gt;Safer &lt;code&gt;this&lt;/code&gt; behavior&lt;/h1&gt;
&lt;p&gt;Arrow functions use lexical &lt;code&gt;this&lt;/code&gt;, meaning they capture &lt;code&gt;this&lt;/code&gt; from the surrounding scope.&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-typescript&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-type&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;Example&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;variable-member&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;42&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;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;4&quot;&gt;  &lt;span class=&quot;function-method&quot;&gt;method&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;&amp;lbrace;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;5&quot;&gt;    &lt;span class=&quot;function-call&quot;&gt;setTimeout&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;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;6&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-builtin&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable-member&quot;&gt;value&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;comment&quot;&gt;// Works&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;line&quot; data-line=&quot;7&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;div class=&quot;line&quot; data-line=&quot;8&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;9&quot;&gt;&lt;span class=&quot;punctuation-bracket&quot;&gt;&amp;rbrace;&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With a traditional function, &lt;code&gt;this&lt;/code&gt; would be undefined or unexpected unless manually bound. Arrow functions eliminate an entire class of common JavaScript bugs.&lt;/p&gt;
&lt;h1&gt;Better fit for functional patterns&lt;/h1&gt;
&lt;p&gt;Arrow functions integrate naturally with higher-order functions:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-typescript&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;function&quot;&gt;double&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-parameter&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type-builtin&quot;&gt;number&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;variable-parameter&quot;&gt;x&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;2&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;numbers&lt;/span&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&quot;&gt;double&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;This style scales well in functional and compositional code.&lt;/p&gt;
&lt;h1&gt;When to use function declarations&lt;/h1&gt;
&lt;p&gt;Function declarations still make sense when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You intentionally rely on hoisting&lt;/li&gt;
&lt;li&gt;You want a clearly named recursive function&lt;/li&gt;
&lt;li&gt;You define class methods&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;They’re not wrong — just more situational.&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Using &lt;code&gt;const&lt;/code&gt; with arrow functions promotes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Explicit execution order&lt;/li&gt;
&lt;li&gt;Immutability&lt;/li&gt;
&lt;li&gt;Safer &lt;code&gt;this&lt;/code&gt; semantics&lt;/li&gt;
&lt;li&gt;Consistency across modern codebases&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For most TypeScript projects, it’s a sensible default. Use &lt;code&gt;function&lt;/code&gt; deliberately when its behavior is exactly what you need.&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/typescript&quot;&gt;#typescript&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-03-05T18:00:00Z</published>
    <id>https://www.yellowduck.be/posts/why-prefer-const-arrow-functions-over-function-declarations-in-typescript</id>
    <title>🐥 Why prefer const arrow functions over function declarations in TypeScript?</title>
    <updated>2026-03-05T18:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/your-go-tests-probably-dont-need-a-mocking-library"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;No shade against mocking libraries like &lt;a href=&quot;https://github.com/uber-go/mock&quot;&gt;gomock&lt;/a&gt; or &lt;a href=&quot;https://github.com/vektra/mockery&quot;&gt;mockery&lt;/a&gt;. I use them all the time, both at work and outside. But one thing I’ve noticed is that generating mocks often leads to poorly designed tests and increases onboarding time for a codebase.&lt;/p&gt;
&lt;p&gt;Also, since almost no one writes tests by hand anymore and instead generates them with LLMs, the situation gets more dire. These ghosts often pull in all kinds of third-party libraries to mock your code, simply because they were trained on a lot of hastily written examples on the web.&lt;/p&gt;
&lt;p&gt;So the idea of this post isn’t to discourage using mocking libraries. Rather, it’s to show that even if your codebase already has a mocking library in the dependency chain, not all of your tests need to depend on it. Below are a few cases where I tend not to use any mocking library and instead leverage the constructs that Go gives us.&lt;/p&gt;
&lt;p&gt;This does require some extra song and dance with the language, but in return, we gain more control over our tests and reduce the chance of encountering &lt;a href=&quot;https://en.wikipedia.org/wiki/Action_at_a_distance_(computer_programming)&quot;&gt;spooky action at a distance&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://rednafi.com/go/mocking-libraries-bleh/&quot;&gt;Continue reading on &lt;strong&gt;rednafi.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/testing&quot;&gt;#testing&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-03-05T14:00:00Z</published>
    <id>https://www.yellowduck.be/posts/your-go-tests-probably-dont-need-a-mocking-library</id>
    <title>🔗 Your Go tests probably don&apos;t need a mocking library</title>
    <updated>2026-03-05T14:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/why-senior-engineers-let-bad-projects-fail"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;When I was a junior engineer, my manager would occasionally confide his frustrations to me in our weekly 1:1s. He would point out a project another team was working on and say, “I don’t believe that project will go anywhere, they’re solving the wrong problem.” I used to wonder, “But you are very senior, why don’t you just go and speak to them about your concerns?” It felt like a waste of his influence to not say anything.&lt;/p&gt;
&lt;p&gt;So it’s quite ironic that I found myself last week explaining to a mentee why I thought a sister team’s project would have to pivot because they’d made a poor early design choice. And he rightfully asked me the same question I had years ago: “why don’t you just tell them your opinion?” It’s been on my mind ever since because I realized I’d changed my stance on it a lot over the years.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The answer is that being right and being effective are different.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In large companies, speaking up about what you see as a “bad project” is a good thing. But only in moderation. Sometimes the mark of seniority is realizing that arguing with people who won’t listen isn’t worth it; it’s better to save your counsel.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://lalitm.com/post/why-senior-engineers-let-bad-projects-fail/&quot;&gt;Continue reading on &lt;strong&gt;lalitm.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;/p&gt;</content>
    <published>2026-03-05T09:00:00Z</published>
    <id>https://www.yellowduck.be/posts/why-senior-engineers-let-bad-projects-fail</id>
    <title>🔗 Why senior engineers let bad projects fail</title>
    <updated>2026-03-05T09:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/thinking-elixir-podcast-294-compile-times-language-servers-and-python-oh-my"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;News includes Expert LSP releasing its first RC, Elixir v1.20 compile time improvements up to 20% faster, Livebook Desktop moving to Tauri with Linux support, a new erlang-python library for ML/AI integration, and more!&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://podcast.thinkingelixir.com/294&quot;&gt;Continue reading on &lt;strong&gt;podcast.thinkingelixir.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/python&quot;&gt;#python&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/elixir&quot;&gt;#elixir&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/linux&quot;&gt;#linux&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-03-04T18:00:00Z</published>
    <id>https://www.yellowduck.be/posts/thinking-elixir-podcast-294-compile-times-language-servers-and-python-oh-my</id>
    <title>🔗 Thinking Elixir podcast 294: compile times, language servers, and Python, oh my!</title>
    <updated>2026-03-04T18:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/software-estimation-building-takes-longer-than-you-think"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;For software engineers, this scenario is all-too familiar…&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Partner&lt;/strong&gt;: Hey can we build this feature?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dev&lt;/strong&gt;: Yeah that should be easy.&lt;/p&gt;
&lt;p&gt;…3 weeks later…&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Partner&lt;/strong&gt;: Hey where’s that feature?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dev&lt;/strong&gt;: Turns out there was a bit more to it than we thought.&lt;/p&gt;
&lt;p&gt;Why is this so common? When it comes to software estimation, why do we consistently underestimate how long something will take, and more importantly, why do we continue to do so even when we should’ve learned our lesson? Well, it’s more complicated than you think. &lt;em&gt;winks insufferably&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://revelry.co/insights/software-estimation-everything-takes-longer/&quot;&gt;Continue reading on &lt;strong&gt;revelry.co&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-03-04T14:00:00Z</published>
    <id>https://www.yellowduck.be/posts/software-estimation-building-takes-longer-than-you-think</id>
    <title>🔗 Software estimation – building takes longer than you think</title>
    <updated>2026-03-04T14:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/my-current-setup-for-laravel-php-and-ai-development-2026-edition"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;After posting a screenshot, I often get questions about which editor, font or tools I&apos;m using. Instead of replying to those questions individually I&apos;ve decided to just write down the settings and apps that I&apos;m using.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://freek.dev/3006-my-current-setup-for-laravel-php-and-ai-development-2026-edition&quot;&gt;Continue reading on &lt;strong&gt;freek.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/laravel&quot;&gt;#laravel&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/php&quot;&gt;#php&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/terminal&quot;&gt;#terminal&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-03-04T09:00:00Z</published>
    <id>https://www.yellowduck.be/posts/my-current-setup-for-laravel-php-and-ai-development-2026-edition</id>
    <title>🔗 My current setup for Laravel, PHP and AI development (2026 edition)</title>
    <updated>2026-03-04T09:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/mrpopov-elixir-phoenix-optimisations-in-iphone-safari"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;If you’re running a Phoenix LiveView application and your iOS Safari users are experiencing frozen pages, slow reconnects, or 10+ second hangs after returning from sleep — this article is for you.&lt;/p&gt;
&lt;p&gt;I’ve been running a production Phoenix LiveView application. The app worked great on desktop, but iPhone users kept reporting the same thing: the page would freeze or hang for several seconds after switching back from another app or unlocking their phone. Sometimes the page would never recover at all.&lt;/p&gt;
&lt;p&gt;Here’s what was actually happening and how I fixed it.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://mrpopov.com/posts/elixir-phoenix-optimisations-iphone-safari/&quot;&gt;Continue reading on &lt;strong&gt;mrpopov.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/css&quot;&gt;#css&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/elixir&quot;&gt;#elixir&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/phoenix&quot;&gt;#phoenix&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/html&quot;&gt;#html&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-03-03T18:00:00Z</published>
    <id>https://www.yellowduck.be/posts/mrpopov-elixir-phoenix-optimisations-in-iphone-safari</id>
    <title>🔗 Elixir Phoenix optimisations in iPhone Safari</title>
    <updated>2026-03-03T18:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link rel="alternate" href="https://www.yellowduck.be/posts/how-i-use-claude-code"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;I’ve been using &lt;a href=&quot;https://docs.anthropic.com/en/docs/claude-code&quot;&gt;Claude Code&lt;/a&gt; as my primary development tool for approx 9 months, and the workflow I’ve settled into is radically different from what most people do with AI coding tools. Most developers type a prompt, sometimes use plan mode, fix the errors, repeat. The more terminally online are stitching together ralph loops, mcps, gas towns (remember those?), etc. The results in both cases are a mess that completely falls apart for anything non-trivial.&lt;/p&gt;
&lt;p&gt;The workflow I’m going to describe has one core principle: &lt;strong&gt;never let Claude write code until you’ve reviewed and approved a written plan&lt;/strong&gt;. This separation of planning and execution is the single most important thing I do. It prevents wasted effort, keeps me in control of architecture decisions, and produces significantly better results with minimal token usage than jumping straight to code.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://boristane.com/blog/how-i-use-claude-code/&quot;&gt;Continue reading on &lt;strong&gt;boristane.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/ai&quot;&gt;#ai&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-03-03T14:00:00Z</published>
    <id>https://www.yellowduck.be/posts/how-i-use-claude-code</id>
    <title>🔗 How I use Claude Code</title>
    <updated>2026-03-03T14:00:00Z</updated>
  </entry>
</feed>