<?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-08-24T17:00:00Z</updated>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/claude-code-skills-automate-repetitive-linux-tasks-with-custom-skills" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Most people assume Claude Code figures out every task by reading your prompt. However, it can automate repetitive sysadmin tasks using skills—structured folders containing instructions, allowing for rapid execution of recurring tasks. This article delves into the essentials of Claude Code Skills, including installation of pre-built skills from Anthropic’s marketplace, creating custom skills, and testing their effectiveness. Readers will learn how to structure a &lt;code&gt;SKILL.md&lt;/code&gt; file effectively, ensuring that Claude identifies and utilizes skills seamlessly. It outlines best practices for writing skills, common mistakes to avoid, and troubleshooting tips to enhance automation and streamline workflows within Linux environments. Whether you&apos;re managing repetitive tasks or developing new skills, this guide offers practical advice for leveraging Claude Code effectively in system administration.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://www.tecmint.com/claude-code-skills-linux/&quot;&gt;Continue reading on &lt;strong&gt;www.tecmint.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/linux&quot;&gt;#linux&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/sysadmin&quot;&gt;#sysadmin&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-24T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/claude-code-skills-automate-repetitive-linux-tasks-with-custom-skills</id>
    <title>🔗 Claude Code skills: automate repetitive Linux tasks with custom skills</title>
    <updated>2026-08-24T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/reviewing-ai-code-is-not-a-viable-argument" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;A strong skepticism towards LLM Coding Assistants arises from their fundamental flaws in code generation. Thomas Depierre critically examines the argument that thorough code reviews can mitigate the issues with AI-generated code. He highlights that relying on these tools often feels like mentoring an inexperienced intern. The inherent limitations of LLMs, including inaccuracies and inconsistent performance, lead to excessive human review that may not yield the efficiency promised by proponents. Empirical evidence underscores that a senior developer can only effectively review a limited amount of code in a fixed time frame, severely hampering productivity. As such, the suggestion to simply review all AI-produced code raises more questions about its practicality and effectiveness than it answers. The call for empirical research on the efficiency of human reviewers handling LLM output remains critical for validating such tools&apos; efficacy in software development.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://www.softwaremaxims.com/blog/reviewing-ai-code&quot;&gt;Continue reading on &lt;strong&gt;www.softwaremaxims.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-08-24T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/reviewing-ai-code-is-not-a-viable-argument</id>
    <title>🔗 Reviewing AI code is not a viable argument</title>
    <updated>2026-08-24T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/a-pdf-parser-from-the-80s-beats-claude-and-they-use-it-internally" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Tesseract, an OCR tool from 1985, outperformed modern AI models when parsing complex PDF files, as highlighted in Fayner Brack&apos;s article. After struggling with a scanned CIA PDF using advanced models like those from Google and Anthropic, Brack discovered that Tesseract was ten times faster and more accurate. The article reveals how Claude integrates Tesseract via &lt;code&gt;PyTesseract&lt;/code&gt;, relying on a combination of heuristics and manual engineering to achieve optimal results when dealing with different types of PDFs. The best practices for PDF parsing, including diagnostic checks and choosing the right tools for scanned versus born-digital documents, underscore the importance of selecting appropriate technologies. Ultimately, Brack demonstrates that sometimes the oldest tools can be the most effective, challenging the notion that newer models always outperform them. Engineers know the value of utilizing the right tool for each specific task, rather than uniformly applying AI solutions everywhere.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://fagnerbrack.com/a-pdf-parser-from-the-80s-beats-claude-and-they-use-it-internally-8ee45a533e80&quot;&gt;Continue reading on &lt;strong&gt;fagnerbrack.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/pdf&quot;&gt;#pdf&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-08-24T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/a-pdf-parser-from-the-80s-beats-claude-and-they-use-it-internally</id>
    <title>🔗 A PDF parser from the 80s beats Claude (and they use it internally)</title>
    <updated>2026-08-24T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/modernizing-my-dotfiles" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;The latest update on the author&apos;s dotfiles reveals a quest for better organization and efficiency. After almost a decade of use, a recent hardware defect prompted a weekend overhaul focused on improving structure and automation. Goals included cleaning folder structures, updating the &lt;code&gt;Brewfile&lt;/code&gt;, and creating a private repo for sensitive configurations like SSH keys. The author drew inspiration from other dotfile experts, simplifying their setup while utilizing tools like Dotbot for symlinks. This modernized system now incorporates AI-driven scripts for smoother workflows and aims to package all settings for version control. The meticulous effort transforms their configurations into a more maintainable and user-friendly setup for future projects.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://stefanzweifel.dev/posts/2026/07/07/modernizing-my-dotfiles/&quot;&gt;Continue reading on &lt;strong&gt;stefanzweifel.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/git&quot;&gt;#git&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/mac&quot;&gt;#mac&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/dotnet&quot;&gt;#dotnet&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-23T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/modernizing-my-dotfiles</id>
    <title>🔗 Modernizing my dotfiles</title>
    <updated>2026-08-23T17: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-achieve-pruning-when-querying-by-non-partitioned-columns-in-postgresql" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Can querying by non-partition key columns really benefit from partition pruning in PostgreSQL? This article details techniques to improve performance when filtering by non-partition key columns, using practical examples to illustrate how clever indexing and database constraints can lead to effective pruning. The author highlights key strategies—like utilizing local indexes and communicating with the database optimizer through check constraints—to achieve significant performance gains. Insights are drawn from a real-world scenario where event logging across multiple partitions showcases the challenges and solutions offered by a well-structured database design. Readers interested in optimizing PostgreSQL queries for better speed and efficiency will find this guide particularly useful.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://hakibenita.com/postgresql-partition-pruning&quot;&gt;Continue reading on &lt;strong&gt;hakibenita.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/database&quot;&gt;#database&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/postgresql&quot;&gt;#postgresql&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-23T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/how-to-achieve-pruning-when-querying-by-non-partitioned-columns-in-postgresql</id>
    <title>🔗 How to achieve pruning when querying by non-partitioned columns in PostgreSQL</title>
    <updated>2026-08-23T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/sanding-ui" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Sanding UI explores a practical approach to refining user interfaces through hands-on testing. Jim Nielsen emphasizes the importance of iterative clicking to identify and address subtle usability issues in web development. One example highlights a common UI problem where a gap created by &lt;code&gt;flexbox&lt;/code&gt; led to an unresponsive area between a radio button and its label. Instead of utilizing a gap, he fixed this by applying padding directly to the label, ensuring full interaction. Nielsen likens this process to woodworking, honing a piece until it feels right. His method encourages developers to embrace the tactile nature of UI design, repeatedly testing and refining until the experience feels seamless. This piece resonates with developers looking to enhance UX by focusing on the small, often overlooked details that impact user engagement and satisfaction.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://blog.jim-nielsen.com/2024/sanding-ui/&quot;&gt;Continue reading on &lt;strong&gt;blog.jim-nielsen.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/frontend&quot;&gt;#frontend&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/css&quot;&gt;#css&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-23T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/sanding-ui</id>
    <title>🔗 Sanding UI</title>
    <updated>2026-08-23T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/faster-leaner-mysql-backups-and-restores-with-mysqldump" rel="alternate"/>
    <content type="html">&lt;p&gt;&lt;code&gt;mysqldump&lt;/code&gt; is a perfectly fine tool for many production workloads — until your database grows large enough that backups take too long and restores take even longer. Before you reach for a more exotic solution, there are several flags and session-level variables that can make a meaningful difference with zero infrastructure changes.&lt;/p&gt;
&lt;p&gt;This article focuses on plain MySQL, but shows where to wire up these settings when you use &lt;a href=&quot;https://github.com/spatie/laravel-backup&quot;&gt;spatie/laravel-backup&lt;/a&gt; and &lt;a href=&quot;https://github.com/stefanzweifel/laravel-backup-restore&quot;&gt;stefanzweifel/laravel-backup-restore&lt;/a&gt; in a Laravel project.&lt;/p&gt;
&lt;h1&gt;Where to configure this in Laravel&lt;/h1&gt;
&lt;p&gt;Both packages read dump and restore options from &lt;code&gt;config/database.php&lt;/code&gt;, inside the &lt;code&gt;dump&lt;/code&gt; key of your connection:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-php&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;&lt;span class=&quot;l-comment&quot;&gt;// config/database.php&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;&lt;span class=&quot;l-string&quot;&gt;&amp;#39;mysql&amp;#39;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;    &lt;span class=&quot;l-comment&quot;&gt;// ... standard connection settings ...&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;    &lt;span class=&quot;l-string&quot;&gt;&amp;#39;dump&amp;#39;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;        &lt;span class=&quot;l-string&quot;&gt;&amp;#39;excludeTables&amp;#39;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;l-text&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;        &lt;span class=&quot;l-string&quot;&gt;&amp;#39;useSingleTransaction&amp;#39;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;l-boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;        &lt;span class=&quot;l-string&quot;&gt;&amp;#39;add_extra_option&amp;#39;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&amp;#39;...&amp;#39;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-comment&quot;&gt;// passed to mysqldump (backup)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;10&quot;&gt;        &lt;span class=&quot;l-string&quot;&gt;&amp;#39;options&amp;#39;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&amp;#39;...&amp;#39;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;          &lt;span class=&quot;l-comment&quot;&gt;// passed to mysql client (restore)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;11&quot;&gt;    &lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;12&quot;&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;add_extra_option&lt;/code&gt; is forwarded to &lt;code&gt;mysqldump&lt;/code&gt; by spatie/laravel-backup.&lt;br /&gt;
&lt;code&gt;options&lt;/code&gt; is forwarded to the &lt;code&gt;mysql&lt;/code&gt; client by laravel-backup-restore.&lt;/p&gt;
&lt;p&gt;Everything below maps directly to one of those two keys.&lt;/p&gt;
&lt;h1&gt;Dump-time optimisations&lt;/h1&gt;
&lt;h2&gt;Exclude noisy, non-essential tables&lt;/h2&gt;
&lt;p&gt;Some tables accumulate millions of rows that are useful for debugging but worthless in a restore. Excluding them shrinks the dump file and cuts restore time dramatically.&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-php&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;&lt;span class=&quot;l-string&quot;&gt;&amp;#39;excludeTables&amp;#39;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;    &lt;span class=&quot;l-string&quot;&gt;&amp;#39;telescope_entries&amp;#39;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;    &lt;span class=&quot;l-string&quot;&gt;&amp;#39;telescope_entries_tags&amp;#39;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;    &lt;span class=&quot;l-string&quot;&gt;&amp;#39;telescope_monitoring&amp;#39;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Laravel Telescope is the classic example: it can easily outweigh the rest of your schema combined.&lt;/p&gt;
&lt;h2&gt;Use a single transaction&lt;/h2&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;l-line&quot; data-line=&quot;1&quot;&gt;--single-transaction
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In spatie/laravel-backup this is:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-php&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;&lt;span class=&quot;l-string&quot;&gt;&amp;#39;useSingleTransaction&amp;#39;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;l-boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This wraps the dump in a &lt;code&gt;START TRANSACTION&lt;/code&gt; so InnoDB tables are read from a consistent snapshot without locking them. Essential for any live database.&lt;/p&gt;
&lt;h2&gt;Increase &lt;code&gt;--net-buffer-length&lt;/code&gt;&lt;/h2&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;l-line&quot; data-line=&quot;1&quot;&gt;--net-buffer-length=16777216   # 16 MB (default is 1 MB)
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;mysqldump&lt;/code&gt; groups rows into multi-row &lt;code&gt;INSERT&lt;/code&gt; statements. The default maximum per statement is 1 MB. Raising it to 16 MB reduces the total number of statements in the file, which directly cuts parse and execution time during a restore.&lt;/p&gt;
&lt;p&gt;Pair it with &lt;code&gt;--max_allowed_packet&lt;/code&gt; on the server side:&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;l-line&quot; data-line=&quot;1&quot;&gt;--max_allowed_packet=512M
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without this, large packets are silently rejected.&lt;/p&gt;
&lt;h2&gt;Skip unnecessary LOCK/UNLOCK statements&lt;/h2&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;l-line&quot; data-line=&quot;1&quot;&gt;--skip-add-locks
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;mysqldump&lt;/code&gt; normally wraps each table dump in &lt;code&gt;LOCK TABLES … WRITE&lt;/code&gt; / &lt;code&gt;UNLOCK TABLES&lt;/code&gt;. This is redundant when you are already using &lt;code&gt;--single-transaction&lt;/code&gt; and when you disable table locking at restore time (see below). Removing these statements makes the dump file smaller and the restore faster.&lt;/p&gt;
&lt;h2&gt;Suppress MySQL 8 column statistics&lt;/h2&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;l-line&quot; data-line=&quot;1&quot;&gt;--column-statistics=0
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;MySQL 8&apos;s &lt;code&gt;mysqldump&lt;/code&gt; emits &lt;code&gt;ANALYZE TABLE&lt;/code&gt; statements to update column statistics after each table is imported. On large tables these can block progress noticeably. If you run your own statistics collection after a restore, suppress them.&lt;/p&gt;
&lt;h2&gt;Compress the dump on the wire&lt;/h2&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;l-line&quot; data-line=&quot;1&quot;&gt;--compression-algorithms=zlib
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Compresses data in transit between &lt;code&gt;mysqldump&lt;/code&gt; and the MySQL server. Useful when the client and server are on separate hosts, reducing network I/O. For socket connections the benefit is minimal, but there is no downside.&lt;/p&gt;
&lt;h2&gt;Skip binary log tracking (GTID)&lt;/h2&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;l-line&quot; data-line=&quot;1&quot;&gt;--set-gtid-purged=OFF
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you use GTID-based replication, &lt;code&gt;mysqldump&lt;/code&gt; normally writes &lt;code&gt;SET @@GLOBAL.gtid_purged&lt;/code&gt; into the dump. During a restore this can conflict with an existing &lt;code&gt;gtid_executed&lt;/code&gt; set. Passing &lt;code&gt;OFF&lt;/code&gt; omits that statement and keeps restores clean on secondary or development databases.&lt;/p&gt;
&lt;h2&gt;Avoid table-level locks&lt;/h2&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;l-line&quot; data-line=&quot;1&quot;&gt;--lock-tables=false
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When using &lt;code&gt;--single-transaction&lt;/code&gt;, table-level locking is already unnecessary. Explicitly disabling it avoids a redundant flush.&lt;/p&gt;
&lt;h2&gt;Use &lt;code&gt;--quick&lt;/code&gt; for large tables&lt;/h2&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;l-line&quot; data-line=&quot;1&quot;&gt;--quick
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By default &lt;code&gt;mysqldump&lt;/code&gt; buffers an entire table in memory before writing. &lt;code&gt;--quick&lt;/code&gt; streams one row at a time, keeping memory usage flat regardless of table size. Always enable it for any production database.&lt;/p&gt;
&lt;h1&gt;Restore-time optimisations&lt;/h1&gt;
&lt;p&gt;These are passed to the &lt;code&gt;mysql&lt;/code&gt; client via an &lt;code&gt;--init-command&lt;/code&gt;, which MySQL executes before the dump is processed:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;l-type&quot;&gt;sql_log_bin&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;l-type&quot;&gt;foreign_key_checks&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;l-type&quot;&gt;unique_checks&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;l-type&quot;&gt;autocommit&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In &lt;code&gt;config/database.php&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-php&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;&lt;span class=&quot;l-string&quot;&gt;&amp;#39;options&amp;#39;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&amp;#39;--init-command=&quot;SET sql_log_bin=0; SET foreign_key_checks=0; SET unique_checks=0; SET autocommit=0;&quot;&amp;#39;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;code&gt;SET sql_log_bin=0&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Disables binary logging for the session. There is no point logging every INSERT from a dump file into the binlog — it inflates the binlog and slows the restore. Safe when you control the import and are not relying on the binlog to propagate changes to replicas. This also reduces the amount of disk space needed for the restore.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;SET foreign_key_checks=0&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;MySQL enforces referential integrity row-by-row during inserts. In a full dump, the parent and child rows are all present — you just may not have inserted the parent yet when a child arrives. Disabling this check lets MySQL trust the dump and skip the per-row lookups, which is one of the most impactful restore optimisations available.&lt;/p&gt;
&lt;p&gt;Re-enable it (or restart the session) after the restore to verify the restored data is consistent.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;SET unique_checks=0&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Similar rationale: MySQL normally verifies unique constraints on every insert. A correctly generated dump has no duplicates, so this check is redundant overhead. Disabling it reduces index update cost during the restore.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;SET autocommit=0&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Turns off the implicit &lt;code&gt;COMMIT&lt;/code&gt; after every statement. Combined with the large &lt;code&gt;INSERT&lt;/code&gt; batches produced by &lt;code&gt;--net-buffer-length&lt;/code&gt;, this means the storage engine can batch many rows into a single transaction, dramatically reducing fsync pressure.&lt;/p&gt;
&lt;h1&gt;Putting it together&lt;/h1&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-php&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;&lt;span class=&quot;l-comment&quot;&gt;// config/database.php&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;&lt;span class=&quot;l-string&quot;&gt;&amp;#39;dump&amp;#39;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;    &lt;span class=&quot;l-string&quot;&gt;&amp;#39;excludeTables&amp;#39;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;        &lt;span class=&quot;l-string&quot;&gt;&amp;#39;telescope_entries&amp;#39;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;        &lt;span class=&quot;l-string&quot;&gt;&amp;#39;telescope_entries_tags&amp;#39;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;        &lt;span class=&quot;l-string&quot;&gt;&amp;#39;telescope_monitoring&amp;#39;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;    &lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;10&quot;&gt;    &lt;span class=&quot;l-string&quot;&gt;&amp;#39;useSingleTransaction&amp;#39;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;l-boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;11&quot;&gt;    &lt;span class=&quot;l-string&quot;&gt;&amp;#39;add_extra_option&amp;#39;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&amp;#39;--set-gtid-purged=OFF --lock-tables=false --quick&amp;#39;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;12&quot;&gt;        &lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&amp;#39; --max_allowed_packet=512M --net-buffer-length=16777216&amp;#39;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;13&quot;&gt;        &lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&amp;#39; --compression-algorithms=zlib&amp;#39;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;14&quot;&gt;        &lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&amp;#39; --skip-add-locks --column-statistics=0&amp;#39;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;15&quot;&gt;    &lt;span class=&quot;l-string&quot;&gt;&amp;#39;options&amp;#39;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&amp;#39;--init-command=&quot;SET sql_log_bin=0; SET foreign_key_checks=0; SET unique_checks=0; SET autocommit=0;&quot;&amp;#39;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;16&quot;&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These settings are safe for the vast majority of production MySQL 8 setups. The restore-side flags assume you trust the dump and will verify data integrity via application-level health checks afterwards — which is standard practice regardless of how you import.&lt;/p&gt;
&lt;p&gt;The backup side produces a smaller file, faster. The restore side skips redundant constraint checking and batches commits. Together they can reduce both phases by a significant margin without changing anything about your infrastructure or your backup tooling.&lt;/p&gt;
&lt;h1&gt;More info&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://spatie.be/docs/laravel-backup/v10/introduction&quot;&gt;Spatie Backup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stefanzweifel.dev/posts/2023/06/15/introducing-laravel-backup-restore/&quot;&gt;Introducing laravel-backup-restore
&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/9.7/en/mysqldump.html&quot;&gt;mysqldump — A Database Backup Program&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/9.7/en/mysql.html&quot;&gt;mysql — The MySQL Command-Line Client&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&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/best-practice&quot;&gt;#best-practice&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/devops&quot;&gt;#devops&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-22T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/faster-leaner-mysql-backups-and-restores-with-mysqldump</id>
    <title>🐥 Faster, leaner MySQL backups and restores with mysqldump</title>
    <updated>2026-08-22T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/why-elixir-is-the-best-language-for-llms" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;On a significant benchmark, Elixir outperformed all other languages in large language model (LLM) problem-solving capabilities. Despite being a low-resource language compared to Python or JavaScript, Elixir achieved a 97.5% success rate in solving problems, vastly ahead of second-place Kotlin at 89.5%. The article highlights the strengths of Elixir, including immutability, verified documentation, and a stable ecosystem, all contributing to its efficacy in LLM contexts. The author points out that these features allow models to effectively reason about code, making Elixir an excellent choice for AI-assisted programming.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://codemyspec.com/blog/why-elixir-is-the-best-language-for-llms&quot;&gt;Continue reading on &lt;strong&gt;codemyspec.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/elixir&quot;&gt;#elixir&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-22T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/why-elixir-is-the-best-language-for-llms</id>
    <title>🔗 Why Elixir is the best language for LLMs</title>
    <updated>2026-08-22T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/the-go-architecture-behind-intelligence-api" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Can a Go-powered authentication layer achieve sub-50ms P99 latency? Autheona claims it can, using advanced concurrency techniques and local SQLite caching. The article dives into their architecture, which eliminates bottlenecks by executing multiple validation checks concurrently via Goroutines, avoiding traditional I/O delays. By replacing external database calls with a custom in-memory policy management tree and deploying asynchronous PostgreSQL batching, they demonstrated speed and scalability, even under heavy load. They also explained the economic implications of latency for businesses, revealing potential revenue losses due to increased authentication processing times. The detailed breakdown of their system architecture, including the use of AWS Graviton instances and embedded SQLite, highlights how efficiency and smart design can drastically improve user experience and conversion rates. This is a must-read for developers keen on optimizing backend performance for high-concurrency applications.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://autheona.com/developer-blog/architecture-behind-intelligence-api/&quot;&gt;Continue reading on &lt;strong&gt;autheona.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/database&quot;&gt;#database&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/devops&quot;&gt;#devops&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/auth&quot;&gt;#auth&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-22T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/the-go-architecture-behind-intelligence-api</id>
    <title>🔗 The Go architecture behind intelligence API</title>
    <updated>2026-08-22T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/first-party-image-processing-in-laravel-13-20" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Laravel 13.20 introduces first-party image processing through an immutable, driver-based API for transforming images. This new functionality allows developers to handle uploads, storage, URLs, and raw bytes directly in the framework without relying on third-party libraries. The &lt;code&gt;Image&lt;/code&gt; facade provides methods for various transformations like resizing, cropping, and format conversion while ensuring that each transformation returns a new instance. Additionally, enhancements such as a &lt;code&gt;#[WithoutMiddleware]&lt;/code&gt; controller attribute and separate Redis prefixes for sessions make for a more efficient development experience. This release builds upon the existing Eloquent and queue functionality with features designed to streamline image handling and improve application organization.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://laravel-news.com/laravel-13-20-0&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/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;/p&gt;</content>
    <published>2026-08-21T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/first-party-image-processing-in-laravel-13-20</id>
    <title>🔗 First-party image processing in Laravel 13.20</title>
    <updated>2026-08-21T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/work-loudly" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Working remotely strips away visibility, making it harder to showcase contributions effectively. &quot;Invisible work is, for every practical purpose—promotion, credit, getting pulled into the interesting projects—work that didn’t happen,&quot; notes Ben Balter. He emphasizes the importance of working loudly, making one&apos;s work visible in real time rather than in a post-project summary. By sharing issues, PRs, and progress updates publicly, individuals invite collaboration and prevent miscommunication that can occur when decisions are made behind closed doors. Visible work leads to better recognition and helps others contribute effectively, breaking the cycle of solitary problem-solving. Those who navigate this landscape successfully can transform their careers and their teams by ensuring their efforts are noted and valued as they happen, rather than waiting for a performance review.  This article advocates for proactively communicating work to build a stronger professional presence and collaborative environment.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://ben.balter.com/2026/07/14/work-loudly/&quot;&gt;Continue reading on &lt;strong&gt;ben.balter.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-08-21T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/work-loudly</id>
    <title>🔗 Work loudly</title>
    <updated>2026-08-21T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/the-git-history-command-deserves-more-attention" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;git history&lt;/code&gt; command is an experimental feature that simplifies managing changes in Git. It includes three subcommands: &lt;code&gt;fixup&lt;/code&gt;, &lt;code&gt;reword&lt;/code&gt;, and &lt;code&gt;split&lt;/code&gt;. The &lt;code&gt;fixup&lt;/code&gt; command allows users to correct an old commit while automatically updating all branches that contain that commit. &lt;code&gt;reword&lt;/code&gt; lets users edit an old commit message and intelligently rebases dependent changes. Lastly, &lt;code&gt;split&lt;/code&gt; enables interactive selection of changes from a commit, dividing it into smaller commits. These features enhance user experience without switching to alternatives like &lt;code&gt;jj&lt;/code&gt; and are integrated into the core Git distribution. Despite having limitations, &lt;code&gt;git history&lt;/code&gt; offers significant improvements for managing a Git workflow and holds promise for future enhancements as user needs evolve.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://lalitm.com/post/git-history/&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/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/git&quot;&gt;#git&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-21T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/the-git-history-command-deserves-more-attention</id>
    <title>🔗 The git history command deserves more attention</title>
    <updated>2026-08-21T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/speeding-up-our-php-test-suite-with-a-github-actions-matrix-strategy" rel="alternate"/>
    <content type="html">&lt;p&gt;Our test suite had become a bottleneck. Every push to &lt;code&gt;develop&lt;/code&gt; or &lt;code&gt;main&lt;/code&gt; triggered three separate CI jobs — unit tests, feature tests, and integration tests — each running sequentially after the previous one finished and each downloading the same apt packages from scratch. The result: slow feedback loops and a lot of redundant work.&lt;/p&gt;
&lt;p&gt;This post walks through the changes we made to fix that.&lt;/p&gt;
&lt;h1&gt;The Problem: Three Jobs, Lots of Duplication&lt;/h1&gt;
&lt;p&gt;Before this change, the CI pipeline defined three independent jobs:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-yaml&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-property&quot;&gt;phpunit-unit&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;PHP Unit Tests&quot;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;  &lt;span class=&quot;l-property&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;    &lt;span class=&quot;l-punctuation-delimiter&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l-property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;Install packages&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;      &lt;span class=&quot;l-property&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-punctuation-delimiter&quot;&gt;|&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;        &lt;span class=&quot;l-function-call&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;update&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;        &lt;span class=&quot;l-function-call&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;-y&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;package-a&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;package-b&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;package-c&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;    &lt;span class=&quot;l-punctuation-delimiter&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l-property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;Execute tests&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;      &lt;span class=&quot;l-property&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;php&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;artisan&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;--parallel&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;tests/Unit&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;10&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;11&quot;&gt;&lt;span class=&quot;l-property&quot;&gt;phpunit-feature&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;12&quot;&gt;  &lt;span class=&quot;l-property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;PHP Feature Tests&quot;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;13&quot;&gt;  &lt;span class=&quot;l-property&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;14&quot;&gt;    &lt;span class=&quot;l-comment&quot;&gt;# same apt install block...&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;15&quot;&gt;    &lt;span class=&quot;l-punctuation-delimiter&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l-property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;Execute tests&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;16&quot;&gt;      &lt;span class=&quot;l-property&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;php&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;artisan&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;--parallel&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;tests/Feature&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;17&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;18&quot;&gt;&lt;span class=&quot;l-property&quot;&gt;phpunit-integration&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;19&quot;&gt;  &lt;span class=&quot;l-property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;PHP Integration Tests&quot;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;20&quot;&gt;  &lt;span class=&quot;l-property&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;21&quot;&gt;    &lt;span class=&quot;l-comment&quot;&gt;# same apt install block again...&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;22&quot;&gt;    &lt;span class=&quot;l-punctuation-delimiter&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l-property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;Execute tests&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;23&quot;&gt;      &lt;span class=&quot;l-property&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;php&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;artisan&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;--parallel&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;tests/Integration&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each job was:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Checking out the repo&lt;/strong&gt; independently&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Setting up PHP&lt;/strong&gt; independently&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Installing Composer dependencies&lt;/strong&gt; independently (even with caching, cache restores take time)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Running &lt;code&gt;apt-get install&lt;/code&gt;&lt;/strong&gt; with the same packages — every single time, with no caching&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;Step 1: Collapsing Three Jobs into a Matrix&lt;/h1&gt;
&lt;p&gt;The first change replaced the three separate job definitions with a single &lt;code&gt;phpunit&lt;/code&gt; job that uses GitHub Actions&apos; &lt;code&gt;matrix&lt;/code&gt; strategy:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-yaml&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-property&quot;&gt;phpunit&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;PHP Tests (${{ matrix.suite }})&quot;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;  &lt;span class=&quot;l-property&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;ubuntu-latest&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;  &lt;span class=&quot;l-property&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;    &lt;span class=&quot;l-property&quot;&gt;fail-fast&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-boolean&quot;&gt;false&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;    &lt;span class=&quot;l-property&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;      &lt;span class=&quot;l-property&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;        &lt;span class=&quot;l-punctuation-delimiter&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l-property&quot;&gt;suite&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;unit-a&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;        &lt;span class=&quot;l-punctuation-delimiter&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l-property&quot;&gt;suite&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;unit-b&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;10&quot;&gt;        &lt;span class=&quot;l-punctuation-delimiter&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l-property&quot;&gt;suite&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;feature-a&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;11&quot;&gt;        &lt;span class=&quot;l-punctuation-delimiter&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l-property&quot;&gt;suite&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;feature-b&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;12&quot;&gt;        &lt;span class=&quot;l-punctuation-delimiter&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l-property&quot;&gt;suite&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;Integration&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All suites run in parallel, with &lt;code&gt;fail-fast: false&lt;/code&gt; so a failure in one suite doesn&apos;t cancel the others mid-run.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Execute tests&lt;/code&gt; step became a single parameterised command:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-yaml&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l-property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;Execute tests&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-property&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;php&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;artisan&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;--parallel&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;--testsuite=&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;${{ matrix.suite &lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt; --coverage-clover test-coverage/coverage-${{ matrix.suite &lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-variable-parameter&quot;&gt;.xml&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Why More Suites Instead of Fewer?&lt;/h2&gt;
&lt;p&gt;The original split was coarse: all unit tests ran together, and all feature tests ran together. One large bucket would hold everything up even when most of the tests in it finished quickly.&lt;/p&gt;
&lt;p&gt;By naming suites explicitly in &lt;code&gt;phpunit.xml&lt;/code&gt;, we got finer-grained parallelism:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-xml&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;testsuite&lt;/span&gt; &lt;span class=&quot;l-tag-attribute&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;unit-a&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;directory&lt;/span&gt; &lt;span class=&quot;l-tag-attribute&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;.php&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;l-text&quot;&gt;./tests/Unit/GroupA&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;testsuite&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;testsuite&lt;/span&gt; &lt;span class=&quot;l-tag-attribute&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;unit-b&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;  &lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;directory&lt;/span&gt; &lt;span class=&quot;l-tag-attribute&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;.php&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;l-text&quot;&gt;./tests/Unit/GroupB&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;  &lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;directory&lt;/span&gt; &lt;span class=&quot;l-tag-attribute&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;.php&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;l-text&quot;&gt;./tests/Unit/GroupC&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;testsuite&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;testsuite&lt;/span&gt; &lt;span class=&quot;l-tag-attribute&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;feature-a&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;  &lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;directory&lt;/span&gt; &lt;span class=&quot;l-tag-attribute&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;.php&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;l-text&quot;&gt;./tests/Feature/GroupA&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;10&quot;&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;testsuite&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;11&quot;&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;testsuite&lt;/span&gt; &lt;span class=&quot;l-tag-attribute&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;feature-b&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;12&quot;&gt;  &lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;directory&lt;/span&gt; &lt;span class=&quot;l-tag-attribute&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;.php&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;l-text&quot;&gt;./tests/Feature/GroupB&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;13&quot;&gt;  &lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;directory&lt;/span&gt; &lt;span class=&quot;l-tag-attribute&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;.php&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;l-text&quot;&gt;./tests/Feature/GroupC&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;14&quot;&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;testsuite&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This keeps each matrix leg roughly balanced in terms of test count and makes the overall wall-clock time shorter than when one slow bucket holds everything up.&lt;/p&gt;
&lt;h1&gt;Step 2: Caching apt Packages&lt;/h1&gt;
&lt;p&gt;The most wasteful part of the old setup was reinstalling the same system packages on every runner, for every job, from scratch. &lt;code&gt;apt-get install&lt;/code&gt; isn&apos;t slow in absolute terms, but multiplied across five parallel jobs and every CI run, it adds up.&lt;/p&gt;
&lt;p&gt;We replaced the raw &lt;code&gt;apt install&lt;/code&gt; block with &lt;a href=&quot;https://github.com/awalsh128/cache-apt-pkgs-action&quot;&gt;&lt;code&gt;awalsh128/cache-apt-pkgs-action&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-yaml&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l-property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;Install packages&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-property&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;awalsh128/cache-apt-pkgs-action@latest&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;  &lt;span class=&quot;l-property&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;    &lt;span class=&quot;l-property&quot;&gt;packages&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;package-a package-b package-c&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;    &lt;span class=&quot;l-property&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;${{ hashFiles(&amp;#39;.apt-packages&amp;#39;) }}&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;version&lt;/code&gt; key is tied to a &lt;code&gt;.apt-packages&lt;/code&gt; lockfile committed to the repo. The cache is invalidated only when that file changes — not on every push — so the common case is a fast cache hit rather than a full install.&lt;/p&gt;
&lt;p&gt;Any post-install configuration that previously lived inside the install block becomes its own named step. The action calls it only on a cache miss, so it doesn&apos;t run unnecessarily on cache hits:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-yaml&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;l-property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;Post-install configuration&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-property&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-comment&quot;&gt;# your configuration command here&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;The Downstream Effect: Coverage Merging&lt;/h1&gt;
&lt;p&gt;Any job that previously depended on the three separate job names now depends on the single &lt;code&gt;phpunit&lt;/code&gt; job:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-yaml&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-property&quot;&gt;some-downstream-job&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-property&quot;&gt;needs&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;    &lt;span class=&quot;l-string&quot;&gt;&quot;phpunit&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;   &lt;span class=&quot;l-comment&quot;&gt;# was: phpunit-unit, phpunit-feature, phpunit-integration&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;    &lt;span class=&quot;l-string&quot;&gt;...&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;  &lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the coverage merge step includes one file per suite:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-bash&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-function-call&quot;&gt;./clover-merge&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;coverage.xml&lt;/span&gt;&lt;span class=&quot;l-text&quot;&gt; \
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;l-variable-parameter&quot;&gt;unit-a/coverage-unit-a.xml&lt;/span&gt;&lt;span class=&quot;l-text&quot;&gt; \
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;l-variable-parameter&quot;&gt;unit-b/coverage-unit-b.xml&lt;/span&gt;&lt;span class=&quot;l-text&quot;&gt; \
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;l-variable-parameter&quot;&gt;feature-a/coverage-feature-a.xml&lt;/span&gt;&lt;span class=&quot;l-text&quot;&gt; \
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;l-variable-parameter&quot;&gt;feature-b/coverage-feature-b.xml&lt;/span&gt;&lt;span class=&quot;l-text&quot;&gt; \
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;l-variable-parameter&quot;&gt;integration/coverage-integration.xml&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;What This Changes&lt;/h1&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;3 jobs, one per test type&lt;/td&gt;
&lt;td&gt;5 jobs, all parallel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;apt install&lt;/code&gt; on every run, every job&lt;/td&gt;
&lt;td&gt;Cached apt packages, invalidated only on lockfile change&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coarse path-based test selection&lt;/td&gt;
&lt;td&gt;Named testsuites in &lt;code&gt;phpunit.xml&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~3× duplicated job boilerplate&lt;/td&gt;
&lt;td&gt;Single parameterised job definition&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The wall-clock time for the PHP test stage drops because the legs run concurrently and the apt install step is usually a cache hit. Maintenance is easier too — adding a new suite is a two-line change: one entry in &lt;code&gt;phpunit.xml&lt;/code&gt; and one entry in the matrix &lt;code&gt;include&lt;/code&gt; list.&lt;/p&gt;&lt;p&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/devops&quot;&gt;#devops&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/github&quot;&gt;#github&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-08-20T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/speeding-up-our-php-test-suite-with-a-github-actions-matrix-strategy</id>
    <title>🐥 Speeding up our PHP test suite with a GitHub Actions matrix strategy</title>
    <updated>2026-08-20T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/learning-elixir-error-handling-with-try-rescue-catch-after" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Error handling in Elixir can be approached with two toolkits for different scenarios. &lt;code&gt;try&lt;/code&gt;, &lt;code&gt;catch&lt;/code&gt;, &lt;code&gt;rescue&lt;/code&gt;, and &lt;code&gt;after&lt;/code&gt; serve as a safety net for unexpected failures, while tagged tuples like &lt;code&gt;{:ok, value}&lt;/code&gt; and &lt;code&gt;{:error, reason}&lt;/code&gt; are for predictable issues. Elixir 1.20.1 introduces constructs that allow developers to handle exceptions like &lt;code&gt;RuntimeError&lt;/code&gt; and &lt;code&gt;ArgumentError&lt;/code&gt;. Understanding when to use these tools is essential since overusing them contradicts Elixir&apos;s philosophy of &quot;let it crash.&quot; This article covers practical guidelines for implementing error handling effectively, demonstrating how to rescue exceptions, define custom error types, and clean up after errors gracefully.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://dev.to/abreujp/learning-elixir-error-handling-with-tryrescuecatchafter-55ij&quot;&gt;Continue reading on &lt;strong&gt;dev.to&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/elixir&quot;&gt;#elixir&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-20T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/learning-elixir-error-handling-with-try-rescue-catch-after</id>
    <title>🔗 Learning Elixir: Error Handling with try/rescue/catch/after</title>
    <updated>2026-08-20T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/film-final-frontier-patagonia" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;100 riders journeyed through 3,000 kilometers in 24 days, traversing one of the world&apos;s last great wildernesses. The film &lt;em&gt;SouthBound&lt;/em&gt; captures the spirit of the inaugural edition of Final Frontier Patagonia, featuring cyclists who faced unimaginable physical and mental challenges as they made their way to Ushuaia. It’s more than just a biking adventure; it’s a powerful tale of endurance, friendship, and the unyielding human spirit amid Patagonia&apos;s breathtaking landscapes. Each rider&apos;s story reflects not just the road challenges, but the internal battles we all face in pursuit of our dreams.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://www.gravelstoke.com/gravel-cycling-gear-blog/final-frontier-patagonia-film&quot;&gt;Continue reading on &lt;strong&gt;www.gravelstoke.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/sports&quot;&gt;#sports&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-20T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/film-final-frontier-patagonia</id>
    <title>🔗 Film: Final Frontier Patagonia</title>
    <updated>2026-08-20T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/learning-a-few-things-about-running-sqlite" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;SQLite may seem simple, but it&apos;s a full-fledged database that can be complex. After using it on a Django project, the author highlights several lessons learned. Key takeaways include the importance of running &lt;code&gt;ANALYZE&lt;/code&gt; to significantly improve query speed, especially for larger datasets. Initially surprised by slow queries, the author found that this one command transformed a 5-second wait into a mere 0.05 seconds. The write locks became problematic during clean-up tasks, prompting the use of smaller batches. Additionally, the author shares backup strategies using &lt;code&gt;restic&lt;/code&gt; and &lt;code&gt;litestream&lt;/code&gt;, revealing the challenges of managing backups effectively. Despite its simplicity, ongoing learning with SQLite proves essential for better database management.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://jvns.ca/blog/2026/07/17/learning-about-running-sqlite/&quot;&gt;Continue reading on &lt;strong&gt;jvns.ca&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/database&quot;&gt;#database&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/django&quot;&gt;#django&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/sqlite&quot;&gt;#sqlite&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-19T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/learning-a-few-things-about-running-sqlite</id>
    <title>🔗 Learning a few things about running SQLite</title>
    <updated>2026-08-19T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/git-rebase-i-is-not-that-scary" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;The fear of &lt;code&gt;git rebase -i&lt;/code&gt; often grips junior developers, but it’s time to demystify its true nature. When executing &lt;code&gt;git rebase -i HEAD~4&lt;/code&gt;, nothing destructive happens immediately; it only opens a text file where you can manage commits. This process is essentially a plan to revise commit history, and everything can be undone with &lt;code&gt;git rebase --abort&lt;/code&gt; if needed. The article clarifies how rebasing creates new commits while preserving the old ones, accessible via &lt;code&gt;git reflog&lt;/code&gt;. Conflicts during a rebase are unavoidable but are often easier to resolve since they involve only one commit at a time. Developers are encouraged to explore interactive rebasing freely, particularly during their own feature branch development, to maintain a clean project history. Understanding this tool can significantly enhance a developer&apos;s confidence and efficacy with Git, making the workflow smoother and more efficient.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://cachebag.sh/journal/interactive-rebasing/&quot;&gt;Continue reading on &lt;strong&gt;cachebag.sh&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/git&quot;&gt;#git&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-19T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/git-rebase-i-is-not-that-scary</id>
    <title>🔗 Git rebase -i is not that scary</title>
    <updated>2026-08-19T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/the-integration-loop" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Handing everyone an LLM subscription gives them a model. A shared, self-hosted agent provides a configured environment with company context, connected tools, and clear boundaries. The article argues why companies should own this layer rather than rent it, outlining practical steps to start small. With implementation costs decreasing, businesses now face the challenge of deciding what is worth building. This shift calls for more autonomy and faster iteration, emphasizing that judgment and decision-making are increasingly valuable in the development process.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://madewithlove.com/blog/the-integration-loop/&quot;&gt;Continue reading on &lt;strong&gt;madewithlove.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-08-19T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/the-integration-loop</id>
    <title>🔗 The integration loop</title>
    <updated>2026-08-19T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/supercharge-your-php-apps-with-go-powered-php-extensions" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Can PHP really become faster without diving into C? In this article, Alexandria Daubois explores how Go-powered PHP extensions can enhance performance in Laravel and Symfony apps. The talk showcases building a custom PHP extension using FrankenPHP, eliminating the need for C code. Daubois details the creation of an in-memory LRU cache and how to integrate it as a native feature in Laravel through a custom cache driver. Readers can expect a hands-on approach focusing on real code and PHP internals, gaining insight into the flexibility of both frameworks and PHP&apos;s core runtime. This exploration hints at the immense potential for boosting performance in PHP applications without traditional complexities.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://phpconference.nl/session/supercharge-your-php-apps-with-go-powered-php-extensions/&quot;&gt;Continue reading on &lt;strong&gt;phpconference.nl&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/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-08-18T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/supercharge-your-php-apps-with-go-powered-php-extensions</id>
    <title>🔗 Supercharge your PHP apps with Go-powered PHP extensions</title>
    <updated>2026-08-18T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/go-releases-are-exciting-especially-1-27" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Go 1.27 introduces some thrilling changes and enhancements for developers. It offers improved performance and new features that make developing applications even more efficient. One notable inclusion is the capability to handle generics more effectively, streamlining code and reducing the boilerplate necessary for complex types. The focus on performance optimizations ensures that applications run faster, enhancing user experiences. As Go continues to evolve, the latest release proves to be a useful upgrade that developers will appreciate.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://kerkour.com/golang-release-exciting&quot;&gt;Continue reading on &lt;strong&gt;kerkour.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/announcement&quot;&gt;#announcement&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-18T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/go-releases-are-exciting-especially-1-27</id>
    <title>🔗 Go releases are exciting (especially 1.27)</title>
    <updated>2026-08-18T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/laravel-facade-pattern-explained-build-your-own-custom-facade" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;The article dives deep into how Laravel&apos;s facade pattern operates and its significance in the framework’s architecture. The commonly used facades, which may appear as static method calls, actually resolve to real objects in Laravel’s Service Container. This structure not only simplifies code readability but also enhances the framework&apos;s testability and flexibility. Readers learn about facades&apos; inner workings, why they&apos;re not static, and when they&apos;re the right choice. The piece culminates in a practical guide on creating a custom facade, helping developers grasp both the theoretical and practical aspects of facades in Laravel. By the end, developers are equipped with the knowledge to use and implement facades efficiently, ultimately fostering a better understanding of Laravel&apos;s dependency injection and service management systems.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://laraveldailytips.com/laravel-facade-pattern-explained-build-your-own-custom-facade&quot;&gt;Continue reading on &lt;strong&gt;laraveldailytips.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/pattern&quot;&gt;#pattern&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/laravel&quot;&gt;#laravel&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-18T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/laravel-facade-pattern-explained-build-your-own-custom-facade</id>
    <title>🔗 Laravel facade pattern explained: build your own custom facade</title>
    <updated>2026-08-18T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/advanced-debugging-in-elixir-with-io-inspect" rel="alternate"/>
    <content type="html">&lt;p&gt;When writing Elixir, most developers quickly get familiar with &lt;a href=&quot;https://hexdocs.pm/elixir/IO.html#inspect/2&quot;&gt;&lt;code&gt;IO.inspect/2&lt;/code&gt;&lt;/a&gt; as a quick way to see what&apos;s happening inside their code. But what many overlook is that &lt;code&gt;IO.inspect&lt;/code&gt; is far more powerful than just &quot;print this variable to the console.&quot;&lt;/p&gt;
&lt;p&gt;In fact, with the right options and placement, &lt;code&gt;IO.inspect&lt;/code&gt; can become a precise, highly targeted debugging tool, one that doesn&apos;t interrupt your program flow and works seamlessly with Elixir&apos;s functional pipelines.&lt;/p&gt;
&lt;p&gt;This post will walk through both the fundamentals and advanced patterns for using &lt;code&gt;IO.inspect&lt;/code&gt; effectively. By the end, you&apos;ll know how to control output formatting, label your prints for clarity, debug concurrent processes, and even integrate conditional or file-based inspection.&lt;/p&gt;
&lt;h1&gt;The Basics of &lt;code&gt;IO.inspect&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://hexdocs.pm/elixir/IO.html#inspect/2&quot;&gt;&lt;code&gt;IO.inspect&lt;/code&gt;&lt;/a&gt; is defined like this:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-module&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;\\&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;item&lt;/strong&gt;: any Elixir value you want to inspect.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;opts&lt;/strong&gt;: keyword list of options for controlling how the term is printed as defined in &lt;a href=&quot;https://hexdocs.pm/elixir/Inspect.Opts.html&quot;&gt;&lt;code&gt;Inspect.Opts&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By default, it writes the inspected value to the standard output (&lt;code&gt;:stdio&lt;/code&gt;) and then returns the term unchanged.&lt;/p&gt;
&lt;p&gt;That last part is key. Because it returns the term, you can drop it anywhere in a function or pipeline without breaking the flow.&lt;/p&gt;
&lt;p&gt;Example:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword-function&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Demo&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-keyword-function&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;    &lt;span class=&quot;l-module&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-punctuation-special&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;Alice&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;age: &lt;/span&gt;&lt;span class=&quot;l-number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Prints:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-punctuation-special&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;age: &lt;/span&gt;&lt;span class=&quot;l-number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;Alice&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Caveat: When &lt;code&gt;IO.inspect&lt;/code&gt; Is the Last Call in a Function&lt;/h1&gt;
&lt;p&gt;Because &lt;code&gt;IO.inspect&lt;/code&gt; returns its argument, it also means that if it&apos;s the &lt;strong&gt;last expression in your function&lt;/strong&gt;, that inspected value becomes the function&apos;s return value.&lt;/p&gt;
&lt;p&gt;That&apos;s fine if you &lt;em&gt;intend&lt;/em&gt; to return it, but it can lead to subtle bugs if you were only printing it for debugging and expected a different return.&lt;/p&gt;
&lt;p&gt;Take this example:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword-function&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;fetch_user&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-module&quot;&gt;Repo&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-module&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;  &lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;label: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;Fetched user&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, the function returns the user struct as normal, which is fine.&lt;/p&gt;
&lt;p&gt;Now, consider:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword-function&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;log_and_return&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-module&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;Done!&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This returns &lt;code&gt;&quot;Done!&quot;&lt;/code&gt; instead of, say, &lt;code&gt;:ok&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you want to print something &lt;strong&gt;but return a different value&lt;/strong&gt;, make the return explicit:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword-function&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;log_and_return&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-module&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;Done!&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;  &lt;span class=&quot;l-string-special-symbol&quot;&gt;:ok&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or if you&apos;re in a pipeline:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-variable&quot;&gt;value&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;label: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;Debug&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;&lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-function-call&quot;&gt;do_something_else&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Rule of thumb:&lt;/strong&gt; if &lt;code&gt;IO.inspect&lt;/code&gt; is the last expression in a function, be explicit about what you want to return.&lt;/p&gt;
&lt;h1&gt;Strategic Placement With the Pipe Operator&lt;/h1&gt;
&lt;p&gt;Because &lt;code&gt;IO.inspect&lt;/code&gt; returns its argument, it fits perfectly in the middle of a pipeline:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-variable&quot;&gt;users&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;label: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;Before filtering&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;&lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;&amp;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&amp;&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;&lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;label: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;After filtering&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;&lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;&amp;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&amp;&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This lets you peek into the data flow at exactly the point you want, without rewriting your code into intermediate variables.&lt;/p&gt;
&lt;p&gt;A neat trick is to place multiple &lt;code&gt;IO.inspect&lt;/code&gt; calls at different stages, each with a distinct label, so you can see how the data changes step by step.&lt;/p&gt;
&lt;h1&gt;Using Labels for Clarity&lt;/h1&gt;
&lt;p&gt;Without labels, multiple inspection outputs can be hard to tell apart. The &lt;code&gt;label&lt;/code&gt; option solves this:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-module&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;label: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;After filtering&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Prints:&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;l-line&quot; data-line=&quot;1&quot;&gt;After filtering: [%{name: &quot;Alice&quot;}, %{name: &quot;Bob&quot;}]
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When debugging a pipeline with several inspect points, labels make the output self-describing. This is especially useful when you&apos;re debugging multiple similar data structures in the same run.&lt;/p&gt;
&lt;h1&gt;Pretty Printing and Formatting Output&lt;/h1&gt;
&lt;p&gt;Sometimes, especially with large maps or deeply nested lists, the default single-line output is hard to read. That&apos;s where formatting options defined in &lt;a href=&quot;https://hexdocs.pm/elixir/Inspect.Opts.html&quot;&gt;&lt;code&gt;Inspect.Opts&lt;/code&gt;&lt;/a&gt; come in.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:pretty&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;if set to &lt;code&gt;true&lt;/code&gt; enables pretty printing.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:limit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;limits the number of items that are inspected for tuples, bitstrings, maps, lists and any other collection of items, with the exception of printable strings and printable charlists which use the &lt;code&gt;:printable_limit&lt;/code&gt; option. If you don&apos;t want to limit the number of items to a particular number, use &lt;code&gt;:infinity&lt;/code&gt;. It accepts a positive integer or &lt;code&gt;:infinity&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;50&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:printable_limit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;limits the number of characters that are inspected on printable strings and printable charlists. You can use &lt;a href=&quot;https://hexdocs.pm/elixir/String.html#printable?/1&quot;&gt;&lt;code&gt;String.printable?/1&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://hexdocs.pm/elixir/List.html#ascii_printable?/1&quot;&gt;&lt;code&gt;List.ascii_printable?/1&lt;/code&gt;&lt;/a&gt; to check if a given string or charlist is printable. If you don&apos;t want to limit the number of characters to a particular number, use &lt;code&gt;:infinity&lt;/code&gt;. It accepts a positive integer or &lt;code&gt;:infinity&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;4096&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:width&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;number of characters per line used when pretty is &lt;code&gt;true&lt;/code&gt; or when printing to IO devices. Set to &lt;code&gt;0&lt;/code&gt; to force each item to be printed on its own line. If you don&apos;t want to limit the number of items to a particular number, use &lt;code&gt;:infinity&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;80&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:charlists&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;when &lt;code&gt;:as_charlists&lt;/code&gt; all lists will be printed as charlists, non-printable elements will be escaped. When &lt;code&gt;:as_lists&lt;/code&gt; all lists will be printed as lists. When the default &lt;code&gt;:infer&lt;/code&gt;, the list will be printed as a charlist if it is printable, otherwise as list. See &lt;a href=&quot;https://hexdocs.pm/elixir/List.html#ascii_printable?/1&quot;&gt;&lt;code&gt;List.ascii_printable?/1&lt;/code&gt;&lt;/a&gt; to learn when a charlist is printable.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:infer&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Example:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-variable&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&lt;-&lt;/span&gt; &lt;span class=&quot;l-number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;l-number&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-punctuation-special&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;id: &lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;l-module&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;duplicate&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;x&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;&lt;span class=&quot;l-module&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;pretty: &lt;/span&gt;&lt;span class=&quot;l-boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;limit: &lt;/span&gt;&lt;span class=&quot;l-number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Prints:&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;l-line&quot; data-line=&quot;1&quot;&gt;[
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  %{id: 1, value: &quot;x&quot;},
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;  %{id: 2, value: &quot;xx&quot;},
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;  %{id: 3, value: &quot;xxx&quot;},
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;  %{id: 4, ...},
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;  %{...},
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;  ...
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;]
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we can see the first five entries, and the rest are summarized with &lt;code&gt;...&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;Coloring and Styling Output&lt;/h1&gt;
&lt;p&gt;Elixir&apos;s inspect options support syntax coloring, very handy when your terminal is full of logs.&lt;/p&gt;
&lt;p&gt;Example:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-module&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-string-special-symbol&quot;&gt;syntax_colors: &lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;    &lt;span class=&quot;l-string-special-symbol&quot;&gt;atom: &lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:blue&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;    &lt;span class=&quot;l-string-special-symbol&quot;&gt;string: &lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:green&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;    &lt;span class=&quot;l-string-special-symbol&quot;&gt;number: &lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:red&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;  &lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;  &lt;span class=&quot;l-string-special-symbol&quot;&gt;pretty: &lt;/span&gt;&lt;span class=&quot;l-boolean&quot;&gt;true&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This makes atoms blue, strings green, and numbers red in your console output.&lt;/p&gt;
&lt;p&gt;Colors can be any &lt;a href=&quot;https://hexdocs.pm/elixir/IO.ANSI.html#t:ansidata/0&quot;&gt;&lt;code&gt;IO.ANSI.ansidata/0&lt;/code&gt;&lt;/a&gt; as accepted by &lt;a href=&quot;https://hexdocs.pm/elixir/IO.ANSI.html#format/1&quot;&gt;&lt;code&gt;IO.ANSI.format/1&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you want to use the default colors (like what is used in &lt;code&gt;IEx&lt;/code&gt;), you can use &lt;a href=&quot;https://hexdocs.pm/elixir/IO.ANSI.html#syntax_colors/0&quot;&gt;&lt;code&gt;IO.ANSI.syntax_colors/0&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-module&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-string-special-symbol&quot;&gt;syntax_colors: &lt;/span&gt;&lt;span class=&quot;l-module&quot;&gt;IO.ANSI&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;syntax_colors&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;  &lt;span class=&quot;l-string-special-symbol&quot;&gt;pretty: &lt;/span&gt;&lt;span class=&quot;l-boolean&quot;&gt;true&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The colors are only visible if your terminal supports ANSI colors.&lt;/p&gt;
&lt;h1&gt;Conditional Inspection&lt;/h1&gt;
&lt;p&gt;Sometimes you want to inspect only if a certain condition is true, for example, when a debug flag is enabled or when a value crosses a threshold.&lt;/p&gt;
&lt;p&gt;Here&apos;s an example with a debug flag:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword-function&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;term&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;get_env&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:my_app&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:debug&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;    &lt;span class=&quot;l-module&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;term&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;label: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;Debug&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;  &lt;span class=&quot;l-variable&quot;&gt;term&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can use this function like this:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-variable&quot;&gt;users&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;&amp;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&amp;&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;&lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-function-call&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;&lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;&amp;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&amp;&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can toggle the output by setting &lt;code&gt;config :my_app, :debug, true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;Capturing Inspect Output Instead of Printing&lt;/h1&gt;
&lt;p&gt;If you want to get the inspected form of a value without printing it, use &lt;a href=&quot;https://hexdocs.pm/elixir/Inspect.html#inspect/2&quot;&gt;&lt;code&gt;inspect/2&lt;/code&gt;&lt;/a&gt; (without the &lt;code&gt;IO.&lt;/code&gt; prefix):&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-variable&quot;&gt;string_representation&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;pretty: &lt;/span&gt;&lt;span class=&quot;l-boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is useful if you want to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Write the debug output to a file&lt;/li&gt;
&lt;li&gt;Send it to a logging service&lt;/li&gt;
&lt;li&gt;Include it in an exception message&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-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-module&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;write!&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;debug.log&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;pretty: &lt;/span&gt;&lt;span class=&quot;l-boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also redirect &lt;code&gt;IO.inspect&lt;/code&gt; output to another device:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-module&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;label: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;Debug&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;device: &lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:stderr&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Debugging Concurrency and Async Code&lt;/h1&gt;
&lt;p&gt;When you use &lt;code&gt;IO.inspect&lt;/code&gt; in asynchronous code, the output may arrive out of order, making it hard to follow. Adding identifiers or timestamps can help.&lt;/p&gt;
&lt;p&gt;Example:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-variable&quot;&gt;tasks&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&lt;-&lt;/span&gt; &lt;span class=&quot;l-number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;l-number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;    &lt;span class=&quot;l-module&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;-&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;      &lt;span class=&quot;l-module&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;label: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;PID &lt;/span&gt;&lt;span class=&quot;l-string-special&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;l-string-special&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;      &lt;span class=&quot;l-type&quot;&gt;:timer&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;      &lt;span class=&quot;l-variable&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;id&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;10&quot;&gt;&lt;span class=&quot;l-module&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&amp;&lt;/span&gt;&lt;span class=&quot;l-module&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Prints:&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;l-line&quot; data-line=&quot;1&quot;&gt;PID 3: #PID&lt;0.112.0&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;PID 2: #PID&lt;0.111.0&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;PID 1: #PID&lt;0.110.0&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Including the PID or a unique request ID in your label helps you trace which output belongs to which process.&lt;/p&gt;
&lt;h1&gt;Advanced Trick: Inspecting and Pattern Matching in One Go&lt;/h1&gt;
&lt;p&gt;You can combine pattern matching with inspection to see exactly what&apos;s being matched:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-punctuation-special&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;id: &lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;label: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;User before insert&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This inspects the &lt;code&gt;user&lt;/code&gt; variable before pattern matching extracts the &lt;code&gt;id&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can also place &lt;code&gt;IO.inspect&lt;/code&gt; inside a guard to conditionally print:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-punctuation-special&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;role: &lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:admin&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;label: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;Admin user&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;  &lt;span class=&quot;l-comment&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:ok&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this last example, &quot;Admin user&quot; will only be printed if the user has the role &lt;code&gt;:admin&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;Using &lt;code&gt;dbg/2&lt;/code&gt; for Richer Inspection&lt;/h1&gt;
&lt;p&gt;Since Elixir &lt;strong&gt;1.14&lt;/strong&gt;, we have &lt;a href=&quot;https://hexdocs.pm/elixir/Kernel.html#dbg/2&quot;&gt;&lt;code&gt;dbg/2&lt;/code&gt;&lt;/a&gt;, a built-in debugging helper that works like &lt;code&gt;IO.inspect&lt;/code&gt; but &lt;strong&gt;also shows the code expression&lt;/strong&gt; that produced the value.&lt;/p&gt;
&lt;p&gt;You can use it like this:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-variable&quot;&gt;users&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;&amp;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&amp;&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-function-call&quot;&gt;dbg&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which prints:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-variable&quot;&gt;users&lt;/span&gt; &lt;span class=&quot;l-comment&quot;&gt;#=&gt; [%{active: false, name: &quot;Alice&quot;}, %{active: true, name: &quot;Bob&quot;}]&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;&amp;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&amp;&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-comment&quot;&gt;#=&gt; [%{active: true, name: &quot;Bob&quot;}]&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This makes it much easier to understand &lt;em&gt;where&lt;/em&gt; in your code the inspected value is coming from, especially when you&apos;re inspecting multiple similar-looking values.&lt;/p&gt;
&lt;p&gt;You can also pass options similar to &lt;code&gt;IO.inspect&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-function-call&quot;&gt;dbg&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;label: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;Active users&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;pretty: &lt;/span&gt;&lt;span class=&quot;l-boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here are the key differences with &lt;code&gt;IO.inspect&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Shows the code expression&lt;/strong&gt; automatically.&lt;/li&gt;
&lt;li&gt;Output format is slightly more verbose.&lt;/li&gt;
&lt;li&gt;Same return behavior, returns the inspected value, so you can keep it in a pipeline.&lt;/li&gt;
&lt;li&gt;Best for &lt;strong&gt;interactive debugging&lt;/strong&gt; during development.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;When &lt;code&gt;IO.inspect&lt;/code&gt; Is Not Enough&lt;/h1&gt;
&lt;p&gt;While &lt;code&gt;IO.inspect&lt;/code&gt; is a fantastic quick-and-dirty tool, there are times when you need more powerful debugging:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://hexdocs.pm/iex/IEx.html#pry/0&quot;&gt;&lt;code&gt;IEx.pry&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt;: drops you into an interactive REPL inside the running process.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.erlang.org/doc/apps/observer/observer.html#start/0&quot;&gt;&lt;code&gt;:observer.start/0&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt;: Erlang&apos;s GUI for monitoring processes, memory, and more.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The trick is to know when to reach for &lt;code&gt;IO.inspect&lt;/code&gt; and when to switch to one of these other tools.&lt;/p&gt;
&lt;h1&gt;Setting Default Options for &lt;code&gt;IO.inspect&lt;/code&gt; in IEx&lt;/h1&gt;
&lt;p&gt;When using &lt;code&gt;IEx&lt;/code&gt;, you can configure the default options for &lt;code&gt;IO.inspect&lt;/code&gt;. You can do this using the &lt;a href=&quot;https://hexdocs.pm/iex/IEx.html#configure/1&quot;&gt;&lt;code&gt;IEx.configure/1&lt;/code&gt;&lt;/a&gt; function:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-module&quot;&gt;IEx&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;configure&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-string-special-symbol&quot;&gt;colors: &lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;    &lt;span class=&quot;l-string-special-symbol&quot;&gt;syntax_colors: &lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;      &lt;span class=&quot;l-string-special-symbol&quot;&gt;number: &lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:light_yellow&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;      &lt;span class=&quot;l-string-special-symbol&quot;&gt;atom: &lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:light_cyan&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;      &lt;span class=&quot;l-string-special-symbol&quot;&gt;string: &lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:light_black&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;      &lt;span class=&quot;l-string-special-symbol&quot;&gt;boolean: &lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:red&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;      &lt;span class=&quot;l-string-special-symbol&quot;&gt;nil: &lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:magenta&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:bright&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;    &lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;10&quot;&gt;    &lt;span class=&quot;l-string-special-symbol&quot;&gt;ls_directory: &lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:cyan&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;11&quot;&gt;    &lt;span class=&quot;l-string-special-symbol&quot;&gt;ls_device: &lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:yellow&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;12&quot;&gt;    &lt;span class=&quot;l-string-special-symbol&quot;&gt;doc_code: &lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:green&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;13&quot;&gt;    &lt;span class=&quot;l-string-special-symbol&quot;&gt;doc_inline_code: &lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:magenta&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;14&quot;&gt;    &lt;span class=&quot;l-string-special-symbol&quot;&gt;doc_headings: &lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:cyan&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:underline&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;15&quot;&gt;    &lt;span class=&quot;l-string-special-symbol&quot;&gt;doc_title: &lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:cyan&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:bright&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:underline&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;16&quot;&gt;  &lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;17&quot;&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can easily put this in your &lt;a href=&quot;https://hexdocs.pm/iex/IEx.html#module-configuring-the-shell&quot;&gt;&lt;code&gt;.iex.exs&lt;/code&gt;&lt;/a&gt; file so that it&apos;s applied automatically every time you open the shell.&lt;/p&gt;
&lt;h1&gt;Creating a Reusable Inspect Helper&lt;/h1&gt;
&lt;p&gt;Here&apos;s a neat way to wrap &lt;code&gt;IO.inspect/2&lt;/code&gt; into your own helper module with preferred defaults. This way, you can keep consistent inspection output without repeating options everywhere:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword-function&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;MyApp.Debug&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-comment&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;l-comment&quot;&gt;moduledoc&lt;/span&gt; &lt;span class=&quot;l-comment&quot;&gt;&quot;&quot;&quot;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;  Convenience wrapper around &lt;/span&gt;&lt;span class=&quot;l-markup-raw&quot;&gt;`IO.inspect/2`&lt;/span&gt;&lt;span class=&quot;l-comment&quot;&gt; with sensible defaults.
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;  &quot;&quot;&quot;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;  &lt;span class=&quot;l-constant&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;l-constant&quot;&gt;default_opts &lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;    &lt;span class=&quot;l-string-special-symbol&quot;&gt;label: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;DEBUG&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;    &lt;span class=&quot;l-string-special-symbol&quot;&gt;pretty: &lt;/span&gt;&lt;span class=&quot;l-boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;    &lt;span class=&quot;l-string-special-symbol&quot;&gt;limit: &lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:infinity&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;10&quot;&gt;    &lt;span class=&quot;l-string-special-symbol&quot;&gt;width: &lt;/span&gt;&lt;span class=&quot;l-number&quot;&gt;120&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;11&quot;&gt;    &lt;span class=&quot;l-string-special-symbol&quot;&gt;syntax_colors: &lt;/span&gt;&lt;span class=&quot;l-module&quot;&gt;IO.ANSI&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;syntax_colors&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;12&quot;&gt;  &lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;13&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;14&quot;&gt;  &lt;span class=&quot;l-comment&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;l-comment&quot;&gt;doc&lt;/span&gt; &lt;span class=&quot;l-comment&quot;&gt;&quot;&quot;&quot;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;15&quot;&gt;  Inspects a value with default debug options.
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;16&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;17&quot;&gt;  This is pipe-friendly: it returns the given value unchanged
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;18&quot;&gt;  after inspecting it.
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;19&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;20&quot;&gt;&lt;/span&gt;&lt;span class=&quot;l-markup-heading-2&quot;&gt;  ## Examples
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;21&quot;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;22&quot;&gt;&lt;span class=&quot;l-markup-raw-block&quot;&gt;      iex&gt; [1, 2, 3]
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;23&quot;&gt;&lt;/span&gt;    &lt;span class=&quot;l-markup-raw-block&quot;&gt;  ...&gt; |&gt; Enum.map(&amp;(&amp;1 * 2))
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;24&quot;&gt;&lt;/span&gt;    &lt;span class=&quot;l-markup-raw-block&quot;&gt;  ...&gt; |&gt; MyApp.Debug.inspect()
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;25&quot;&gt;&lt;/span&gt;    &lt;span class=&quot;l-markup-raw-block&quot;&gt;  [2, 4, 6]
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;26&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;l-comment&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;27&quot;&gt;  &lt;span class=&quot;l-keyword-function&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;term&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;\\&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;28&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;term&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;29&quot;&gt;    &lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-module&quot;&gt;Keyword&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-constant&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;l-constant&quot;&gt;default_opts&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;30&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;31&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can use it like this:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-variable&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-variable&quot;&gt;users&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;  &lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;&amp;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;&amp;&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;active?&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;  &lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;MyApp.Debug&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;label: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;Active users&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;  &lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;&amp;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&amp;&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;  &lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;MyApp.Debug&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;label: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;User names&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You get pretty printing (&lt;code&gt;pretty: true&lt;/code&gt;) by default.&lt;/li&gt;
&lt;li&gt;Lists and maps are not truncated (&lt;code&gt;limit: :infinity&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;A label is always shown (&lt;code&gt;DEBUG&lt;/code&gt; unless overridden).&lt;/li&gt;
&lt;li&gt;You can still override any option per call.&lt;/li&gt;
&lt;li&gt;It works fine when it&apos;s in a pipeline.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Handy Visual Studio Code snippets&lt;/h1&gt;
&lt;p&gt;To make using &lt;code&gt;IO.inspect&lt;/code&gt; faster and more consistent, you can configure editor snippets in Visual Studio Code so you don&apos;t have to type repetitive boilerplate each time.&lt;/p&gt;
&lt;p&gt;In Visual Studio Code, open:&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;l-line&quot; data-line=&quot;1&quot;&gt;Code -&gt; Settings… -&gt; Configure Snippets -&gt; Elixir
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and add the following snippet definitions:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-json&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-property&quot;&gt;&quot;inpsect&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;    &lt;span class=&quot;l-property&quot;&gt;&quot;prefix&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;lin&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;    &lt;span class=&quot;l-property&quot;&gt;&quot;body&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;IO.inspect($1, label: &lt;/span&gt;&lt;span class=&quot;l-string-escape&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;l-string-escape&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;, pretty: true)&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;    &lt;span class=&quot;l-property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;IO.inspect with label.&quot;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;  &lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;  &lt;span class=&quot;l-property&quot;&gt;&quot;inpsectSelectedText&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;    &lt;span class=&quot;l-property&quot;&gt;&quot;prefix&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;sin&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;    &lt;span class=&quot;l-property&quot;&gt;&quot;body&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;IO.inspect($TM_SELECTED_TEXT, label: &lt;/span&gt;&lt;span class=&quot;l-string-escape&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;${TM_SELECTED_TEXT/(.*)/${1:/upcase}/}$0&lt;/span&gt;&lt;span class=&quot;l-string-escape&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;, pretty: true)&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;10&quot;&gt;    &lt;span class=&quot;l-property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;IO.inspect with selected text as label.&quot;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;11&quot;&gt;  &lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;12&quot;&gt;  &lt;span class=&quot;l-property&quot;&gt;&quot;pipeInspect&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;13&quot;&gt;    &lt;span class=&quot;l-property&quot;&gt;&quot;prefix&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;pin&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;14&quot;&gt;    &lt;span class=&quot;l-property&quot;&gt;&quot;body&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;|&gt; IO.inspect(label: &lt;/span&gt;&lt;span class=&quot;l-string-escape&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;l-string-escape&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;, pretty: true)&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;15&quot;&gt;    &lt;span class=&quot;l-property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;IO.inspect with pipe.&quot;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;16&quot;&gt;  &lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;17&quot;&gt;  &lt;span class=&quot;l-property&quot;&gt;&quot;pipeInspectWithFileReference&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;18&quot;&gt;    &lt;span class=&quot;l-property&quot;&gt;&quot;prefix&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;pinf&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;19&quot;&gt;    &lt;span class=&quot;l-property&quot;&gt;&quot;body&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;|&gt; IO.inspect(label: &lt;/span&gt;&lt;span class=&quot;l-string-escape&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;$TM_FILEPATH:$TM_LINE_NUMBER$0&lt;/span&gt;&lt;span class=&quot;l-string-escape&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;, pretty: true)&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;20&quot;&gt;    &lt;span class=&quot;l-property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;IO.inspect with pipe and file reference.&quot;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;21&quot;&gt;  &lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;22&quot;&gt;  &lt;span class=&quot;l-property&quot;&gt;&quot;inspectFromClipboard&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;23&quot;&gt;    &lt;span class=&quot;l-property&quot;&gt;&quot;prefix&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;cin&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;24&quot;&gt;    &lt;span class=&quot;l-property&quot;&gt;&quot;body&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;IO.inspect($CLIPBOARD, label: &lt;/span&gt;&lt;span class=&quot;l-string-escape&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;${CLIPBOARD/(.*)/${1:/upcase}/}$0&lt;/span&gt;&lt;span class=&quot;l-string-escape&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;, pretty: true)&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;25&quot;&gt;    &lt;span class=&quot;l-property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;IO.inspect clipboard content.&quot;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;26&quot;&gt;  &lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;27&quot;&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With these in place, you&apos;ll have short prefixes to insert commonly used &lt;code&gt;IO.inspect/2&lt;/code&gt; patterns:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prefix&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IO.inspect&lt;/code&gt; with label&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IO.inspect&lt;/code&gt; using selected text as label&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IO.inspect&lt;/code&gt; in a pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pinf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IO.inspect&lt;/code&gt; in a pipeline with file and line reference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IO.inspect&lt;/code&gt; clipboard content&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This way, you can quickly drop in debug output with consistent formatting, colors, and labels, without breaking your flow.&lt;/p&gt;
&lt;p&gt;Suppose you are transforming a list of user maps and want to check intermediate results inside a pipeline:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-variable&quot;&gt;users&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;&amp;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;&amp;&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;&lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;label: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;After filter&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;pretty: &lt;/span&gt;&lt;span class=&quot;l-boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;&lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;&amp;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;&amp;&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the snippet defined above, you don&apos;t have to type all that. Just type &lt;code&gt;pin&lt;/code&gt;, hit &lt;strong&gt;Tab&lt;/strong&gt;, and you get:&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;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;label: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;pretty: &lt;/span&gt;&lt;span class=&quot;l-boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can immediately type your label (e.g. &lt;code&gt;&quot;After filter&quot;&lt;/code&gt;) and continue coding. This keeps your debugging consistent, colorful, and fast, without breaking your flow.&lt;/p&gt;
&lt;h1&gt;Best practices&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Use labels liberally&lt;/strong&gt;, unlabeled output is harder to parse.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Limit output&lt;/strong&gt; for large collections, use &lt;code&gt;limit&lt;/code&gt; and &lt;code&gt;pretty&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Avoid leaving it in production&lt;/strong&gt; unless intentional.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wrap it&lt;/strong&gt; in helper functions when you want conditional control.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tag concurrent output&lt;/strong&gt; with PIDs, timestamps, or request IDs.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hexdocs.pm/credo/overview.html&quot;&gt;&lt;strong&gt;Credo&lt;/strong&gt;&lt;/a&gt; can be used to detect unintentional calls to &lt;code&gt;IO.inspect&lt;/code&gt; in a CI/CD pipeline.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;IO.inspect&lt;/code&gt; may look like a humble debugging tool, but in Elixir it&apos;s a powerful way to see what&apos;s going on without breaking your code&apos;s flow. By combining it with labels, formatting, conditional output, and process context, you can get precise insights into your program&apos;s behavior, all without leaving your editor.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/pattern&quot;&gt;#pattern&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/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-08-17T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/advanced-debugging-in-elixir-with-io-inspect</id>
    <title>🐥 Advanced debugging in Elixir with IO.inspect</title>
    <updated>2026-08-17T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/my-notes-on-three-years-of-coding-with-ai" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;How has AI shaped the coding landscape over three years? Mike Zornek shares his journey from casual use to heavy reliance on tools like Copilot and Claude Code. Starting with a light-hearted rap song generated in 2023, Zornek transitioned to utilizing AI for actual development tasks while grappling with the ethical implications of AI-generated pull requests. Throughout this time, he emphasized the importance of human oversight and the need for clear communication about AI&apos;s role in the coding process. His experiences reveal both the challenges and opportunities presented by AI in software engineering, encouraging engineers not just to adapt but to actively engage with these emerging technologies as they evolve.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://mikezornek.com/posts/2026/7/three-years-coding-with-ai/&quot;&gt;Continue reading on &lt;strong&gt;mikezornek.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/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-08-17T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/my-notes-on-three-years-of-coding-with-ai</id>
    <title>🔗 My notes on three years of coding with AI</title>
    <updated>2026-08-17T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/1password-for-claude-give-claude-access-without-giving-up-your-credentials" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;AI agents are evolving into powerful tools that can perform tasks on behalf of users, but the challenge of securely accessing accounts remains. The article introduces &lt;code&gt;1Password for Claude&lt;/code&gt;, which allows Claude to complete browser tasks that require logins without exposing user credentials. Built on a zero-exposure architecture, it ensures that credentials are not stored in the model, and every authentication request requires user consent. This innovative approach aligns with a new security model where users can grant AI agents the access they need for specific tasks while keeping credentials invisible to the agent.&lt;/p&gt;
&lt;p&gt;Moreover, the Agentic Mode feature enhances control when AI agents operate within browsers, ensuring that only approved logins are accessible. This approach safeguards against unauthorized access and maintains user trust. As AI capabilities grow, adopting governed access strategies like those offered by 1Password is crucial for maintaining security in a collaborative digital landscape.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://1password.com/blog/1password-for-claude&quot;&gt;Continue reading on &lt;strong&gt;1password.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/auth&quot;&gt;#auth&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-17T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/1password-for-claude-give-claude-access-without-giving-up-your-credentials</id>
    <title>🔗 1Password for Claude: give Claude access without giving up your credentials</title>
    <updated>2026-08-17T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/detecting-full-table-scans-with-sqlite" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;What if you could detect full table scans in SQLite effortlessly? The article dives into how SQLite collects information about prepared statements, allowing developers to figure out if a query caused a full table scan after it was executed. This could have been beneficial for lobste.rs during a recent performance issue. With practical code examples, the author shows how to create a table, insert records, and utilize the statement statistics to check for full scans. By adding indices, the performance improves instantly, as demonstrated with another query. There’s also a thought-provoking suggestion to integrate this functionality into Rails, potentially enhancing the debugging process in development environments.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://tenderlovemaking.com/2026/07/15/detecting-full-table-scans-with-sqlite/&quot;&gt;Continue reading on &lt;strong&gt;tenderlovemaking.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/database&quot;&gt;#database&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/sqlite&quot;&gt;#sqlite&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-16T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/detecting-full-table-scans-with-sqlite</id>
    <title>🔗 Detecting full table scans with SQLite</title>
    <updated>2026-08-16T17:00:00Z</updated>
  </entry>
</feed>