<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Gabriel Ortuño — Staff Engineering Notes]]></title><description><![CDATA[Notes from a Staff Engineer on Elixir, Phoenix, observability, distributed systems and software architecture.]]></description><link>https://www.arctarus.com</link><image><url>https://substackcdn.com/image/fetch/$s_!SAJ3!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F700f9bc1-87e8-44f9-8972-c68f9fb526ea_1024x1024.png</url><title>Gabriel Ortuño — Staff Engineering Notes</title><link>https://www.arctarus.com</link></image><generator>Substack</generator><lastBuildDate>Mon, 21 Sep 2026 22:47:46 GMT</lastBuildDate><atom:link href="https://www.arctarus.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Gabriel Ortuño]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[arctarsus@proton.me]]></webMaster><itunes:owner><itunes:email><![CDATA[arctarsus@proton.me]]></itunes:email><itunes:name><![CDATA[Gabriel Ortuño]]></itunes:name></itunes:owner><itunes:author><![CDATA[Gabriel Ortuño]]></itunes:author><googleplay:owner><![CDATA[arctarsus@proton.me]]></googleplay:owner><googleplay:email><![CDATA[arctarsus@proton.me]]></googleplay:email><googleplay:author><![CDATA[Gabriel Ortuño]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[When BEAM Is the Right Runtime]]></title><description><![CDATA[A practical guide for engineers evaluating Erlang and Elixir for resilient systems]]></description><link>https://www.arctarus.com/p/when-beam-is-the-right-runtime</link><guid isPermaLink="false">https://www.arctarus.com/p/when-beam-is-the-right-runtime</guid><dc:creator><![CDATA[Gabriel Ortuño]]></dc:creator><pubDate>Sun, 07 Jun 2026 06:00:36 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!xDIv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F953180c4-2c0a-4e5a-a5df-3b3e2a69c726_1024x608.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Engineers evaluating BEAM often start with the wrong question:</p><blockquote><p>Is BEAM faster than the runtime I already know?</p></blockquote><p>It is a reasonable question, but it frames the problem the wrong way.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.arctarus.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Gabriel Ortu&#241;o &#8212; Staff Engineering Notes! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>BEAM was not designed to win every benchmark. Its roots are in Erlang, a language created for telecom systems where downtime was unacceptable. That history shaped a runtime built around a different problem: keeping many concurrent, stateful, independent activities running when parts of the system fail.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xDIv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F953180c4-2c0a-4e5a-a5df-3b3e2a69c726_1024x608.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xDIv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F953180c4-2c0a-4e5a-a5df-3b3e2a69c726_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!xDIv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F953180c4-2c0a-4e5a-a5df-3b3e2a69c726_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!xDIv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F953180c4-2c0a-4e5a-a5df-3b3e2a69c726_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!xDIv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F953180c4-2c0a-4e5a-a5df-3b3e2a69c726_1024x608.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xDIv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F953180c4-2c0a-4e5a-a5df-3b3e2a69c726_1024x608.png" width="1024" height="608" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/953180c4-2c0a-4e5a-a5df-3b3e2a69c726_1024x608.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:608,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xDIv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F953180c4-2c0a-4e5a-a5df-3b3e2a69c726_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!xDIv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F953180c4-2c0a-4e5a-a5df-3b3e2a69c726_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!xDIv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F953180c4-2c0a-4e5a-a5df-3b3e2a69c726_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!xDIv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F953180c4-2c0a-4e5a-a5df-3b3e2a69c726_1024x608.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>That is the key to understanding Erlang, Elixir, and OTP. The ecosystem is built around a specific view of software: systems fail, networks fail, dependencies fail, processes get into bad states, and production does not care about your clean abstraction boundaries.</p><p>The answer is not to prevent every failure. It is to make failure contained, observable, and recoverable.</p><p>So the better evaluation question is:</p><blockquote><p>Does my system need to manage many long-lived activities that own state, communicate, fail, and recover independently?</p></blockquote><p>If the answer is yes, BEAM deserves serious consideration.</p><h1>The constraints explain the runtime</h1><p>A useful way to understand the runtime is to look at the constraints it optimizes for.</p><p>BEAM assumes your system may need many concurrent activities, long-lived stateful processes, isolation between components, failure containment, recovery without restarting the whole system, soft real-time responsiveness, runtime observability, distributed communication, and operational continuity.</p><p>Those constraints explain the design choices: processes must be cheap, memory should be isolated, failures need to be observable, recovery needs to be explicit, and scheduling must prevent one process from monopolizing the system.</p><p>The design is coherent because these choices reinforce each other. Lightweight processes, message passing, supervision, per-process memory, and observability are not isolated features. They are different answers to the same core constraint: preserving service continuity under concurrency and failure.</p><div class="callout-block" data-callout="true"><p style="text-align: center;">BEAM&#8217;s basic unit of architecture is not a thread, a class, or a request handler. It is an isolated process that owns state, communicates through messages, and can be restarted when it fails.</p></div><h1>The core design: process ownership</h1><p>The most important architectural shift is process ownership.</p><p>In many ecosystems, engineers design around classes, modules, services, handlers, queues, repositories, and thread pools. In Erlang and Elixir systems, you also design around processes.</p><p>A process is a lightweight runtime-managed unit of execution. It is not an operating system process. It has its own memory, its own mailbox, and its own lifecycle. Processes communicate by sending messages.</p><p>This changes the way you model a system.</p><p>Instead of asking only:</p><blockquote><p>Which module should contain this logic?</p></blockquote><p>You ask:</p><blockquote><p>Who owns this state?</p></blockquote><p>This question is fundamental.</p><p>A process can own a user session, a WebSocket connection, a payment workflow, a device connection, a background job, or a live dashboard subscription. It can even own the state of a single moving car on a live map. This is not just implementation detail. It becomes architecture.</p><p>I have seen this most clearly in real-time operational tools built with Phoenix LiveView. The hard part was not rendering one screen or processing one request quickly. The interesting part was coordinating operational state, user interactions, real-time updates, and long-lived activities safely. When each moving entity can be modeled independently, BEAM&#8217;s process model becomes a very natural fit.</p><p>A process is not merely a concurrent task. It is often the owner of a piece of state, a protocol, a lifecycle, and a failure boundary.</p><p>This is one of the strongest reasons to consider BEAM: runtime boundaries can align naturally with domain boundaries.</p><h1>Isolation by default</h1><p>Processes do not share memory in the normal way. They communicate through messages.</p><p>A process owns its memory. Other processes cannot casually mutate it. To interact, they send messages.</p><p>The benefit is significant: failure and state are easier to contain.</p><p>In a BEAM system, one process getting into a bad state does not necessarily corrupt the rest of the system. It can crash. A supervisor can restart it. Other processes can continue.</p><p>This is the foundation of &#8220;let it crash&#8221;.</p><p>The phrase is often misunderstood. It does not mean &#8220;write careless code&#8221;. It means accepting that sometimes the safest recovery strategy is to terminate a faulty process and restart a clean one.</p><p>This only works when failure boundaries are small. BEAM gives you those boundaries.</p><h1>Supervision as a design tool</h1><p>In many systems, error handling is local: a function returns an error, a method throws an exception, a task fails, or infrastructure restarts the whole process.</p><p>OTP adds another layer: supervision.</p><p>A supervisor is a process whose job is to start, monitor, and restart child processes according to a strategy. The strategy defines what happens when a child fails. Should only that process restart? Should all related processes restart? How many restarts are acceptable before escalating?</p><p>This is not just a library convenience. It is a system design tool.</p><p>A supervision tree describes the failure topology of your application. It forces architectural questions that many systems postpone:</p><ul><li><p>What state is transient?</p></li><li><p>What state must be persisted?</p></li><li><p>What can be rebuilt?</p></li><li><p>Which components are independent?</p></li><li><p>Which components must fail together?</p></li><li><p>What is the smallest safe restart boundary?</p></li><li><p>Who owns recovery?</p></li></ul><p>In a well-designed BEAM system, recovery is not scattered randomly across `try/catch`, retries, callbacks, and infrastructure probes. Recovery is part of the application structure.</p><p>For long-running systems, this is a serious advantage.</p><h1>Mailboxes as the communication model</h1><p>Every BEAM process has a mailbox. Other processes send messages to it. The receiving process chooses when and how to handle them.</p><p>This gives you a clean model for asynchronous communication. It also enables selective receive, where a process can wait for messages matching a certain pattern while leaving other messages in the mailbox.</p><p>Mailboxes make process communication explicit. Instead of shared mutable state, processes exchange messages and react to them.</p><p>This reinforces the broader model: state is owned locally, communication is explicit, and each process has a clear boundary.</p><h1>Per-process garbage collection</h1><p>One of the runtime&#8217;s most important choices is per-process garbage collection.</p><p>Each process has its own heap. Garbage collection usually happens per process.</p><p>As a result, a process with a small heap can be collected quickly. A process that allocates heavily pays much of its own cost. Other processes can often continue running.</p><p>This fits the broader philosophy: isolate not only state and failure, but also memory management cost.</p><p>For highly concurrent systems with many small independent processes, this can reduce latency coupling between unrelated activities.</p><p>It also gives engineers a more concrete debugging model. Instead of asking only:</p><blockquote><p>Why is memory growing?</p></blockquote><p>You can ask:</p><blockquote><p>Which process owns this memory?</p></blockquote><p>That is a much more useful operational question.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!OC-u!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8832a06d-5f7c-4668-b9f5-2cd2920e5be9_1448x1086.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OC-u!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8832a06d-5f7c-4668-b9f5-2cd2920e5be9_1448x1086.png 424w, https://substackcdn.com/image/fetch/$s_!OC-u!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8832a06d-5f7c-4668-b9f5-2cd2920e5be9_1448x1086.png 848w, https://substackcdn.com/image/fetch/$s_!OC-u!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8832a06d-5f7c-4668-b9f5-2cd2920e5be9_1448x1086.png 1272w, https://substackcdn.com/image/fetch/$s_!OC-u!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8832a06d-5f7c-4668-b9f5-2cd2920e5be9_1448x1086.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!OC-u!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8832a06d-5f7c-4668-b9f5-2cd2920e5be9_1448x1086.png" width="1448" height="1086" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8832a06d-5f7c-4668-b9f5-2cd2920e5be9_1448x1086.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1086,&quot;width&quot;:1448,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1625921,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://gabrielortuno.substack.com/i/199996215?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8832a06d-5f7c-4668-b9f5-2cd2920e5be9_1448x1086.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!OC-u!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8832a06d-5f7c-4668-b9f5-2cd2920e5be9_1448x1086.png 424w, https://substackcdn.com/image/fetch/$s_!OC-u!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8832a06d-5f7c-4668-b9f5-2cd2920e5be9_1448x1086.png 848w, https://substackcdn.com/image/fetch/$s_!OC-u!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8832a06d-5f7c-4668-b9f5-2cd2920e5be9_1448x1086.png 1272w, https://substackcdn.com/image/fetch/$s_!OC-u!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8832a06d-5f7c-4668-b9f5-2cd2920e5be9_1448x1086.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1>When BEAM is the right fit and when it is not</h1><p>No runtime is a universal answer, and BEAM is no exception.</p><p>BEAM is especially attractive when the domain naturally contains many independent concurrent entities: real-time communication, chat, presence, collaborative applications, live dashboards, IoT device management, game servers, workflow orchestration, background jobs, WebSocket-heavy applications, event-driven state machines, and long-lived business processes.</p><p>In these systems, the hard part is often not one request. The hard part is lifecycle.</p><p>Something starts. It receives events. It owns state. It waits. It times out. It talks to other components. It may fail. It may need to restart. It may need to recover state. It may need to notify others.</p><p>This is where BEAM&#8217;s process model shines.</p><p>A useful heuristic:</p><div class="callout-block" data-callout="true"><p>If your architecture diagram has many boxes representing independent things that live over time, BEAM is worth evaluating.</p></div><p>The opposite is also true. If your architecture diagram is mostly stateless request/response handlers plus database calls, BEAM can still work well, especially with Phoenix, but its unique runtime advantages may matter less.</p><p>Be more cautious if your core workload is CPU-heavy computation, machine learning inference, large-scale numerical processing, video/audio processing, high-performance compression or encryption, large mutable in-memory data structures, low-level systems programming, ultra-low-latency trading-style systems, or workloads dominated by SIMD/vectorization.</p><p>Also be careful if the key libraries are much stronger outside the BEAM ecosystem, or if the team has no appetite to learn OTP deeply.</p><p>BEAM can still participate in those systems. It can coordinate them, supervise them, expose APIs around them, and manage long-running workflows. But it is often not the best place to run the hottest CPU loop.</p><p>A mature BEAM architecture often uses BEAM for orchestration and resilience, while delegating specialized computation to external services, ports, or carefully controlled native code.</p><p>This is not a failure. It is good architecture.</p><h1>The traps newcomers underestimate</h1><p>A few traps are especially common for engineers coming from other ecosystems. They are not syntax problems. They are runtime-model problems.</p><p>The most important one is <strong>ignoring mailbox growth</strong>. A growing mailbox usually means producers are faster than the consumer, or the consumer is blocked or overloaded. If you do not monitor mailboxes, the problem may only become visible through memory pressure, latency, or availability issues.</p><p>The second trap is <strong>turning `GenServer`s into bottlenecks</strong>. A `GenServer` processes one message at a time. If you put too much responsibility into one process, the system may look concurrent while behaving like it has a hidden global lock.</p><p>The third trap is <strong>believing supervision solves data recovery</strong>. Restarting a process gives you a clean process, not restored business state. You still need persistence, idempotency, replay, compensation, or reconstruction strategies.</p><p>The fourth trap is <strong>overusing synchronous calls</strong>. `GenServer.call` is useful, but too many synchronous dependencies create latency chains and failure propagation. Isolation disappears when every process is waiting for another one.</p><p>The fifth trap is <strong>using processes as if they were objects</strong>. A process has lifecycle, state, mailbox, failure semantics, and scheduling behavior. The better question is not &#8220;which process exposes this method?&#8221;, but &#8220;which process owns this state and failure boundary?&#8221;</p><p>The sixth trap is <strong>trusting distributed Erlang blindly</strong>. It is powerful, but it is not automatically the right choice for every service boundary. Security, network partitions, topology, latency, and operational ownership matter.</p><p>The real learning curve is not Elixir syntax. The real learning curve is OTP: supervision, process design, failure semantics, message flow, observability, releases, and production diagnosis.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UbIC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cda9d12-1899-4f67-bfae-f0a94d2c8cb3_1491x1055.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UbIC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cda9d12-1899-4f67-bfae-f0a94d2c8cb3_1491x1055.png 424w, https://substackcdn.com/image/fetch/$s_!UbIC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cda9d12-1899-4f67-bfae-f0a94d2c8cb3_1491x1055.png 848w, https://substackcdn.com/image/fetch/$s_!UbIC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cda9d12-1899-4f67-bfae-f0a94d2c8cb3_1491x1055.png 1272w, https://substackcdn.com/image/fetch/$s_!UbIC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cda9d12-1899-4f67-bfae-f0a94d2c8cb3_1491x1055.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UbIC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cda9d12-1899-4f67-bfae-f0a94d2c8cb3_1491x1055.png" width="1456" height="1030" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9cda9d12-1899-4f67-bfae-f0a94d2c8cb3_1491x1055.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1030,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1645301,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://gabrielortuno.substack.com/i/199996215?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cda9d12-1899-4f67-bfae-f0a94d2c8cb3_1491x1055.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!UbIC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cda9d12-1899-4f67-bfae-f0a94d2c8cb3_1491x1055.png 424w, https://substackcdn.com/image/fetch/$s_!UbIC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cda9d12-1899-4f67-bfae-f0a94d2c8cb3_1491x1055.png 848w, https://substackcdn.com/image/fetch/$s_!UbIC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cda9d12-1899-4f67-bfae-f0a94d2c8cb3_1491x1055.png 1272w, https://substackcdn.com/image/fetch/$s_!UbIC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cda9d12-1899-4f67-bfae-f0a94d2c8cb3_1491x1055.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1>Final recommendation</h1><p>Do not evaluate BEAM as a drop-in replacement for your current runtime. Evaluate it as a different way to structure a system.</p><p>Before choosing it for a real project, run a small architectural spike around the runtime model, not just around syntax or framework productivity.</p><p>Model one meaningful part of your domain as processes:</p><ul><li><p>What owns the state?</p></li><li><p>What messages cross the boundaries?</p></li><li><p>What happens when one process crashes?</p></li><li><p>What state must be rebuilt or persisted?</p></li><li><p>What can be supervised safely?</p></li><li><p>What happens if messages arrive faster than they are consumed?</p></li><li><p>Where does CPU-heavy work belong?</p></li></ul><p>If that exercise makes the design clearer, BEAM may be a strong fit.</p><p>If it makes the design feel artificial, or most of the value still lives in request handlers, database queries, and external libraries, another runtime may be a better default.</p><p>That is the practical test.</p><p>BEAM earns its place when process ownership, isolation, supervision, and recovery make the system easier to reason about &#8212; not merely because Elixir is pleasant or the runtime is elegant.</p><h1>Further reading</h1><p>These are good starting points if you want to go deeper into BEAM, Erlang/OTP, and the runtime ideas behind this article:</p><ul><li><p><strong><a href="https://www.erlang.org/blog/a-brief-beam-primer/?utm_source=chatgpt.com">A brief introduction to BEAM</a></strong> &#8212; official Erlang/OTP primer on what BEAM is and how it relates to the Erlang Runtime System.</p></li><li><p><strong><a href="https://blog.stenmans.org/theBeamBook/?utm_source=chatgpt.com">The BEAM Book</a></strong> &#8212; a deep technical reference on BEAM internals, instructions, scheduling, memory, and runtime implementation details.</p></li><li><p><strong><a href="https://www.erlang-in-anger.com/">Erlang in Anger</a></strong> &#8212; Fred H&#233;bert&#8217;s practical guide to operating and debugging Erlang systems in production.</p></li><li><p><strong><a href="https://www.erlang.org/doc/design_principles/des_princ.html">Erlang/OTP Design Principles</a></strong> &#8212; official documentation on OTP behaviours, supervision trees, applications, and releases.</p></li><li><p><strong><a href="https://www.erlang.org/doc/system/sup_princ.html?utm_source=chatgpt.com">Supervisor behaviour documentation</a></strong> &#8212; official reference for OTP supervisors and restart strategies.</p></li><li><p><strong><a href="https://www.erlang.org/doc/system/efficiency_guide.html">Erlang Efficiency Guide</a></strong> &#8212; official guidance on processes, memory, binaries, and performance trade-offs.</p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.arctarus.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Gabriel Ortu&#241;o &#8212; Staff Engineering Notes! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Is Elixir’s Observability Ready for Production? A Guide for Skeptical Engineers]]></title><description><![CDATA[From BEAM introspection to OpenTelemetry: how Elixir actually behaves in production]]></description><link>https://www.arctarus.com/p/is-elixirs-observability-ready-for</link><guid isPermaLink="false">https://www.arctarus.com/p/is-elixirs-observability-ready-for</guid><dc:creator><![CDATA[Gabriel Ortuño]]></dc:creator><pubDate>Sun, 01 Feb 2026 09:17:41 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Da0T!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F602a44db-6a8e-4768-9d84-95a026699e79_1024x608.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Da0T!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F602a44db-6a8e-4768-9d84-95a026699e79_1024x608.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Da0T!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F602a44db-6a8e-4768-9d84-95a026699e79_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!Da0T!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F602a44db-6a8e-4768-9d84-95a026699e79_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!Da0T!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F602a44db-6a8e-4768-9d84-95a026699e79_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!Da0T!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F602a44db-6a8e-4768-9d84-95a026699e79_1024x608.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Da0T!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F602a44db-6a8e-4768-9d84-95a026699e79_1024x608.png" width="1024" height="608" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/602a44db-6a8e-4768-9d84-95a026699e79_1024x608.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:608,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Da0T!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F602a44db-6a8e-4768-9d84-95a026699e79_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!Da0T!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F602a44db-6a8e-4768-9d84-95a026699e79_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!Da0T!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F602a44db-6a8e-4768-9d84-95a026699e79_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!Da0T!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F602a44db-6a8e-4768-9d84-95a026699e79_1024x608.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>As a software engineer coming from ecosystems like the JVM, Go, or Node.js you have a deep understanding of Site Reliability Engineering (SRE) principles and the modern observability stack. You know what it takes to operate systems at scale. When evaluating a new technology like Elixir, a critical question inevitably arises: &#8220;Can I really observe and operate this in production?&#8221; It&#8217;s a valid concern, born from experience with runtimes that can often feel like black boxes.</p><p>This article provides a direct, technically-grounded answer to that question. We will explore how Elixir offers a unique, two-pronged observability strategy: unparalleled real-time introspection thanks to its runtime design, combined with full integration into the standardized world of OpenTelemetry and Prometheus. The goal is to demonstrate that Elixir&#8217;s observability isn&#8217;t just an afterthought; it&#8217;s a powerful combination of deep, intrinsic runtime visibility and modern, standards-based instrumentation.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.arctarus.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Gabriel Ortu&#241;o Notes! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>1. A Different Foundation: Observability by Design in the BEAM VM</h2><p>To understand observability in Elixir, we must first look at its foundation. Elixir runs on the BEAM virtual machine, a core component of the Erlang Run-Time System (ERTS). The BEAM was designed from the ground up for concurrent, fault-tolerant systems, and the ability to inspect a live system is a fundamental part of its architecture, not a feature added later.</p><h4>Lightweight Processes: The Core of Concurrency</h4><p>The core unit of execution in an Elixir application is a &#8220;process.&#8221; Unlike heavyweight OS threads, BEAM processes are extremely lightweight. Each process is an isolated entity with its own stack, heap, and message queue (mailbox), all managed by the BEAM&#8217;s preemptive scheduler. This is fundamentally different from OS threads, which are managed by the kernel and have a much larger memory footprint. The BEAM&#8217;s ability to manage millions of lightweight processes within a single OS process is central to both its concurrency model and its observability, as the state of every single unit of work is visible to the runtime.</p><h4>Built-in Introspection with Observer</h4><p>The most striking feature for engineers new to the BEAM is <code>Observer</code>, a powerful graphical tool that is built directly into the runtime. It allows you to connect to a live Elixir node&#8212;in development or production&#8212;and inspect its internal state in real-time. This is not a third-party add-on; it&#8217;s a core capability of ERTS.</p><p><code>Observer</code> provides critical insights across several areas:</p><ul><li><p><strong>Application Inspection:</strong> Visualize the complete supervision tree of your application. This shows exactly how your processes are structured, which processes are supervising others, and how they are linked for fault tolerance.</p></li><li><p><strong>Process Information:</strong> Get a real-time, sortable list of every process running on the node. For each process, you can see its current memory usage, the length of its message queue, and its reduction count&#8212;a proxy for how much CPU work it has done. This is invaluable for identifying bottlenecks or memory-hungry processes instantly.</p></li><li><p><strong>System Overview:</strong> View detailed statistics about the BEAM itself, including scheduler utilization across all cores, memory allocator statistics for different data types, and I/O activity.</p></li></ul><p>These same introspection capabilities are available programmatically through functions like <code>erlang:process_info/2</code>, allowing you to build custom monitoring and diagnostic tools that tap directly into the runtime&#8217;s own data.</p><p><code>Observer</code> is unparalleled for interactive, live-system debugging, but it is not designed for the automated, long-term, and aggregated analysis required for production SRE practices like alerting and SLO tracking. For that, we turn to the industry-standard three pillars.</p><h2>2. Bridging to the Familiar: The Three Pillars in Elixir</h2><p>While <code>Observer</code> provides a powerful &#8216;point-in-time&#8217; view into the VM, a robust observability strategy also requires historical data and distributed context. This is where the ecosystem bridges from its unique internal capabilities to the familiar &#8216;three pillars&#8217; model, with the <code>telemetry</code> library serving as the cornerstone.</p><h4>The Foundation: <code>telemetry</code></h4><p><code>Telemetry</code> is a lightweight, universal event-dispatching library that has become the standard foundation for instrumentation in Elixir. Key libraries like the Phoenix web framework and the Ecto database wrapper use <code>telemetry</code> to emit standardized events about their internal operations (e.g., request timings, query performance). Other libraries can then attach to these events to generate logs, metrics, or traces. This architecture decouples instrumentation from consumption. Library authors (like the Phoenix team) can emit events without dictating how they are used, allowing operations teams to plug in any number of handlers for metrics, logging, or tracing without modifying the core library code. This is a crucial design choice for a flexible and maintainable ecosystem.</p><h4>The Three Pillars of Observability</h4><p>This <code>telemetry</code>-based approach provides a clean bridge to the three pillars of observability, which are essential for understanding the <em>why</em> behind a system&#8217;s behavior, not just the <em>what</em>.</p><ul><li><p><strong>Logs:</strong> Discrete, timestamped records of events. They are essential for troubleshooting specific occurrences, providing the rich, detailed context needed to understand the steps leading to an error or a specific transaction.</p></li><li><p><strong>Metrics:</strong> Aggregated numerical representations of system health over time (e.g., request latency, error rates, CPU utilization). They are ideal for building dashboards, defining alerts on known thresholds, and analyzing historical trends.</p></li><li><p><strong>Traces:</strong> A detailed view of a single request&#8217;s end-to-end journey as it flows through a distributed system. Traces are crucial for diagnosing latency issues, identifying bottlenecks, and understanding inter-service dependencies in a microservices architecture.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Uk_P!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e4a3a31-c2be-443c-94b5-916bcfb402bb_2752x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Uk_P!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e4a3a31-c2be-443c-94b5-916bcfb402bb_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!Uk_P!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e4a3a31-c2be-443c-94b5-916bcfb402bb_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!Uk_P!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e4a3a31-c2be-443c-94b5-916bcfb402bb_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!Uk_P!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e4a3a31-c2be-443c-94b5-916bcfb402bb_2752x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Uk_P!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e4a3a31-c2be-443c-94b5-916bcfb402bb_2752x1536.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4e4a3a31-c2be-443c-94b5-916bcfb402bb_2752x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:5763332,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://gabrielortuno.substack.com/i/185645230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e4a3a31-c2be-443c-94b5-916bcfb402bb_2752x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Uk_P!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e4a3a31-c2be-443c-94b5-916bcfb402bb_2752x1536.png 424w, https://substackcdn.com/image/fetch/$s_!Uk_P!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e4a3a31-c2be-443c-94b5-916bcfb402bb_2752x1536.png 848w, https://substackcdn.com/image/fetch/$s_!Uk_P!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e4a3a31-c2be-443c-94b5-916bcfb402bb_2752x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!Uk_P!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e4a3a31-c2be-443c-94b5-916bcfb402bb_2752x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The <code>telemetry</code> library provides the universal event bus that allows specialized libraries to transform raw events into the metrics and traces needed for a complete observability picture.</p><h2>3. Logs: Structured Logging as a First-Class Citizen</h2><p>Before discussing metrics and dashboards, it&#8217;s worth addressing logging, because this is often the first observability signal teams rely on in production&#8212;and an area where Elixir is frequently underestimated.</p><p>Elixir&#8217;s logging system is built around the <code>Logger</code> module, which is part of the standard library and deeply integrated with the runtime. While Logger historically emitted plain text logs by default, modern Elixir applications almost universally use <strong>structured logging</strong>, and the ecosystem makes this both straightforward and idiomatic.</p><h3>Structured Logs by Default, Not by Convention</h3><p>Logger supports structured, key&#8211;value metadata natively. Instead of treating logs as unstructured strings that must later be parsed, Elixir encourages attaching contextual metadata&#8212;such as request IDs, user identifiers, or domain-specific fields&#8212;directly to log events.</p><p>This metadata propagates naturally through the process model, meaning contextual information (like a request ID set at the boundary of a web request) is automatically available to all logs emitted by that process and its children. This aligns well with modern logging practices and avoids the need for thread-local hacks or manual context passing common in other ecosystems.</p><h3>Flexible Encoding and Output</h3><p>In production, Logger can be configured to emit logs in <strong>structured formats such as JSON</strong>, making it trivial to integrate with standard log aggregation systems (e.g., Elasticsearch, Loki, or commercial SaaS platforms). This is typically achieved by swapping the default formatter for a JSON formatter, without changing application code.</p><p>Because Logger is part of the runtime and not a third-party abstraction, this configuration is:</p><ul><li><p>Centralized</p></li><li><p>Consistent across the ecosystem</p></li><li><p>Decoupled from any specific log backend</p></li></ul><p>This makes it easy to evolve logging strategies over time&#8212;from local development logs to fully structured production pipelines&#8212;without rewriting instrumentation.</p><h3>Logs as Part of a Cohesive Observability Strategy</h3><p>Crucially, logging in Elixir does not exist in isolation. Logger integrates cleanly with the broader observability stack:</p><ul><li><p>Logs can include trace and span identifiers when OpenTelemetry is enabled</p></li><li><p>Log volume and levels can be dynamically adjusted at runtime</p></li><li><p>Logging remains lightweight, with backpressure handled by the BEAM rather than the application</p></li></ul><p>The result is a logging system that feels modern, production-oriented, and aligned with today&#8217;s expectations: structured by default, context-aware, and easy to route into both open-source and commercial observability platforms.</p><p>When OpenTelemetry is enabled, this structured logging model becomes even more powerful. Trace and span identifiers (<code>trace_id</code>, <code>span_id</code>) can be automatically injected into Logger metadata, allowing logs to be directly correlated with distributed traces. This makes it trivial to pivot from a slow or failing trace to the exact log lines emitted within that span, using standard tooling in both open-source and commercial platforms. For teams used to trace&#8211;log correlation in JVM or Go-based systems, this behavior is fully supported and idiomatic in Elixir.</p><h2>4. Metrics: The Prometheus &amp; Grafana Story</h2><p>For metrics, the Elixir ecosystem has a go-to solution for integrating with the industry-standard Prometheus and Grafana stack: <strong>PromEx</strong>.</p><p>PromEx is a library designed to make exposing Prometheus-compatible metrics from an Elixir application as simple and streamlined as possible. It works by attaching its own handlers to the <code>telemetry</code> events emitted by your application and its dependencies, converting them into the metrics format that Prometheus scrapes.</p><p>The setup and features highlight its power and ease of use:</p><ol><li><p><strong>Simple Configuration:</strong> A single mix task, <code>mix prom_ex.gen.config</code>, generates the necessary configuration module, removing boilerplate and guesswork.</p></li><li><p><strong>Effortless Phoenix Integration:</strong> <code>PromEx.Plug</code> is a simple plug that can be added to your Phoenix endpoint to expose a <code>/metrics</code> endpoint. Prometheus can then be configured to scrape this endpoint automatically.</p></li><li><p><strong>Rich Ecosystem Plugins:</strong> PromEx offers a suite of pre-built plugins that provide deep visibility into the most common parts of an Elixir stack, including:</p><ul><li><p><code>PromEx.Plugins.Beam</code> (for BEAM VM metrics)</p></li><li><p><code>PromEx.Plugins.Phoenix</code> (for web request metrics)</p></li><li><p><code>PromEx.Plugins.Ecto</code> (for database query metrics)</p></li><li><p><code>PromEx.Plugins.Oban</code> (for background job processing metrics)</p></li></ul></li><li><p><strong>Pre-built Dashboards:</strong> Crucially, each plugin comes with a tailored Grafana dashboard. This means you get meaningful, out-of-the-box visualizations for your key metrics without having to build dashboards from scratch.</p></li></ol><h2>5. Tracing: Embracing the OpenTelemetry Standard</h2><p>For distributed tracing, the Elixir community has fully adopted <strong>OpenTelemetry (OTel)</strong>, the Cloud Native Computing Foundation (CNCF) framework that provides a vendor-neutral standard for generating and collecting telemetry data.</p><h4>Core Components and Instrumentation</h4><p>Setting up OTel in a typical Phoenix application involves adding a few key libraries: <code>opentelemetry_api</code>, <code>opentelemetry</code>, and instrumentation-specific packages like <code>opentelemetry_phoenix</code>.</p><p>Instrumentation works in two primary ways:</p><ul><li><p><strong>Automatic Instrumentation:</strong> Libraries like <code>opentelemetry_phoenix</code> listen for standard <code>:telemetry</code> events and automatically create OTel spans from them. This provides a baseline level of tracing for web requests and database queries with minimal setup.</p></li><li><p><strong>Manual Instrumentation:</strong> For business-logic-specific insights, you can create custom spans programmatically using functions like <code>Tracer.with_span</code>. This is essential for tracing business-critical logic that isn&#8217;t captured by default framework events, such as the steps within a complex financial transaction or a multi-stage data processing pipeline.</p></li></ul><h4>Exporting Data via the OTel Collector</h4><p>The best practice for sending telemetry data in production is to use the <strong>OpenTelemetry Collector</strong>. This is a vendor-agnostic agent that receives data from your application (typically via the OTLP protocol) and can then process and export it to any number of backends. This decouples your application from the specific tracing backend (e.g., Jaeger, Zipkin, or a commercial platform), preventing vendor lock-in.</p><h4>A Note on Maturity and Performance</h4><p>The OpenTelemetry SDK for Elixir/Erlang is mature and widely used in production. However, for systems with very high request throughput, some performance tuning may be required. Specifically, operators may need to adjust the configuration of the batch span processor (e.g., <code>OTEL_BSP_SCHEDULE_DELAY_MILLIS</code>) to balance memory usage and latency, ensuring the observability layer itself does not become a bottleneck.</p><p>One important distinction to be explicit about is profiling. Unlike the JVM (JFR) or Go (pprof), the BEAM does not yet offer a ubiquitous, always-on, low-overhead continuous profiler as part of the standard production toolchain. Instead, profiling in Elixir relies on a combination of built-in tools (:eprof, :fprof, :cprof), runtime introspection, and situational diagnostics using Observer or libraries like recon. This means profiling is typically more targeted and investigative rather than continuously sampled. For many teams this is sufficient&#8212;especially when combined with strong metrics and tracing&#8212;but it is a real trade-off to be aware of for workloads where continuous CPU or allocation profiling is a hard requirement.</p><h2>6. Integrating with Commercial Platforms (Datadog, New Relic, AppSignal, etc.)</h2><p>Elixir applications integrate reliably with major commercial observability platforms such as Datadog, New Relic, and AppSignal. For most production systems today, the <strong>recommended and future-proof integration path is OpenTelemetry</strong>, which provides a vendor-neutral foundation for traces, metrics, and logs.</p><p>By instrumenting your application using OpenTelemetry APIs, you decouple observability concerns from any specific backend. Telemetry data is typically exported to an <strong>OpenTelemetry Collector</strong>, which then forwards it to one or more destinations&#8212;self-hosted systems like Jaeger or Prometheus, or commercial platforms such as Datadog or New Relic. Switching backends does not require changes to application code; it is a configuration concern at the Collector level.</p><p>This OTel-first approach has two important implications:</p><ul><li><p><strong>Consistency and portability</strong>: Instrumentation remains stable even if vendors change, pricing shifts, or organizational preferences evolve.</p></li><li><p><strong>Reduced vendor lock-in</strong>: The application depends on open standards rather than proprietary agents or SDKs.</p></li></ul><p>Some platforms also offer <strong>Elixir-specific or vendor-native integrations</strong>. AppSignal, in particular, provides a tightly integrated, Elixir-first experience with minimal setup and strong defaults. This can be an attractive option for teams that prefer a managed solution and are comfortable trading some flexibility for simplicity. However, such integrations typically couple instrumentation more closely to the vendor and make later migrations more expensive.</p><p>At a high level, the trade-off is familiar:</p><ul><li><p><strong>Self-Hosted Open Source (Prometheus / Grafana / Jaeger):</strong> Maximum control and flexibility with no direct vendor costs. Requires operational ownership and expertise.</p></li><li><p><strong>Commercial SaaS Platforms: </strong>Managed infrastructure, advanced analytics, and lower operational overhead. Cost scales with usage.</p></li></ul><p>In practice, many teams adopt a <strong>hybrid model</strong>: open-source tooling for core metrics and dashboards, combined with commercial platforms for tracing, alerting, or cross-service analysis. Elixir&#8217;s OpenTelemetry-based integrations support this model well.</p><h2>7. The Verdict: Is Elixir Production-Ready?</h2><p>So, is Elixir&#8217;s observability stack ready for the demands of modern production systems? The answer is yes. The ecosystem provides a clear two-pronged approach: deep, real-time runtime introspection via the BEAM, combined with full support for industry-standard observability practices through Telemetry, OpenTelemetry, and Prometheus-based tooling.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dBY8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa63a7f00-f594-4782-a036-e84339f25029_1536x2752.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dBY8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa63a7f00-f594-4782-a036-e84339f25029_1536x2752.png 424w, https://substackcdn.com/image/fetch/$s_!dBY8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa63a7f00-f594-4782-a036-e84339f25029_1536x2752.png 848w, https://substackcdn.com/image/fetch/$s_!dBY8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa63a7f00-f594-4782-a036-e84339f25029_1536x2752.png 1272w, https://substackcdn.com/image/fetch/$s_!dBY8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa63a7f00-f594-4782-a036-e84339f25029_1536x2752.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dBY8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa63a7f00-f594-4782-a036-e84339f25029_1536x2752.png" width="1456" height="2609" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a63a7f00-f594-4782-a036-e84339f25029_1536x2752.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:2609,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:6025331,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://gabrielortuno.substack.com/i/185645230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa63a7f00-f594-4782-a036-e84339f25029_1536x2752.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!dBY8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa63a7f00-f594-4782-a036-e84339f25029_1536x2752.png 424w, https://substackcdn.com/image/fetch/$s_!dBY8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa63a7f00-f594-4782-a036-e84339f25029_1536x2752.png 848w, https://substackcdn.com/image/fetch/$s_!dBY8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa63a7f00-f594-4782-a036-e84339f25029_1536x2752.png 1272w, https://substackcdn.com/image/fetch/$s_!dBY8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa63a7f00-f594-4782-a036-e84339f25029_1536x2752.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The BEAM&#8217;s intrinsic visibility enables operators to inspect scheduler behavior, memory usage, and individual process state directly on a live system, answering the question &#8220;what is happening right now?&#8221; with a level of precision that is difficult to achieve in many other runtimes. At the same time, standardized metrics, logs, and traces provide the historical context required for alerting, SLOs, and distributed debugging.</p><p>This contrasts with platforms where the runtime is largely opaque and observability relies primarily on sampling and post-hoc inference. In JVM or Go ecosystems, visibility is often reconstructed from external signals; in the BEAM, it is exposed directly by the runtime itself.</p><p>That said, Elixir&#8217;s observability model will not be a perfect fit for every organization. Teams that depend heavily on always-on, low-overhead continuous profiling, that are unwilling to operate BEAM-specific tooling, or that mandate vendor-specific agents may find a more natural fit elsewhere. Elixir rewards teams willing to understand the runtime it runs on&#8212;and for those teams, it offers a level of operational transparency that is difficult to replicate.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.arctarus.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Gabriel Ortu&#241;o Notes! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Elixir’s Gradual Type System (1.17–1.20): How the Compiler Finally Understands Your Code]]></title><description><![CDATA[How the compiler now reasons about your code and how to take advantage of it]]></description><link>https://www.arctarus.com/p/elixirs-gradual-type-system-117120</link><guid isPermaLink="false">https://www.arctarus.com/p/elixirs-gradual-type-system-117120</guid><dc:creator><![CDATA[Gabriel Ortuño]]></dc:creator><pubDate>Sun, 18 Jan 2026 12:26:28 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!xCmL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9472c3a-0279-4b16-b016-2b2e007445f3_1024x608.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xCmL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9472c3a-0279-4b16-b016-2b2e007445f3_1024x608.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xCmL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9472c3a-0279-4b16-b016-2b2e007445f3_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!xCmL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9472c3a-0279-4b16-b016-2b2e007445f3_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!xCmL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9472c3a-0279-4b16-b016-2b2e007445f3_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!xCmL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9472c3a-0279-4b16-b016-2b2e007445f3_1024x608.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xCmL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9472c3a-0279-4b16-b016-2b2e007445f3_1024x608.png" width="1024" height="608" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f9472c3a-0279-4b16-b016-2b2e007445f3_1024x608.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:608,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xCmL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9472c3a-0279-4b16-b016-2b2e007445f3_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!xCmL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9472c3a-0279-4b16-b016-2b2e007445f3_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!xCmL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9472c3a-0279-4b16-b016-2b2e007445f3_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!xCmL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9472c3a-0279-4b16-b016-2b2e007445f3_1024x608.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Elixir set-theoretic type system</figcaption></figure></div><h2><strong>Why This Matters Now</strong></h2><p>Elixir 1.20 is not just another compiler release; it marks a pivotal moment in the language&#8217;s history. For the first time, the compiler performs <em>sound, flow-sensitive static analysis</em> on idiomatic Elixir code. It doesn&#8217;t just check syntax; it builds a control-flow graph to prove properties about your data, surfacing errors that previously required runtime crashes or extensive test suites to detect.</p><p>This is not about turning Elixir into a statically typed language like Java or Go. It is about evolving the &#8220;Let it Crash&#8221; philosophy into &#8220;Prove it won&#8217;t crash&#8221;. The goal is to write idiomatic Elixir that is now provably safer, easier to refactor, and self-documenting.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.arctarus.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Gabriel Ortu&#241;o Notes! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>If you maintain a large or long-lived codebase, the way you write function heads, data structures, and guards now directly impacts the compiler&#8217;s ability to protect you.</p><div><hr></div><h2><strong>What &#8220;Gradual Typing&#8221; Means in Elixir</strong></h2><p>Elixir&#8217;s type system is built on three pillars:</p><ul><li><p><strong>Gradual</strong>: Types are optional. The system embraces the <code>dynamic()</code> type, allowing valid runtime code to exist even if static types can&#8217;t be proven yet.</p></li><li><p><strong>Set-theoretic</strong>: Types are treated as sets of values. <code>integer()</code> or <code>atom()</code> is a union set. <code>integer()</code> and <code>atom()</code> is an empty set (because no value can be both).</p></li><li><p><strong>Flow-sensitive</strong>: The compiler learns from your code&#8217;s control flow. A guard or a pattern match isn&#8217;t just a runtime check; it is a fact that refines the type of a variable for the rest of the block.</p></li></ul><p><strong>The Critical Distinction</strong>: Unlike Dialyzer, which uses &#8220;Success Typing&#8221; (optimistically assuming code is valid unless proven otherwise), the new system is sound. If it identifies a type violation (e.g., an empty intersection like <code>String</code> AND <code>Integer</code>), it guarantees that the code is buggy.</p><div><hr></div><h2><strong>The Mental Model: Strong Arrows</strong></h2><p>The most important concept to internalize is the strong arrow.</p><p>In many dynamic languages, operators accept any type and return any type. In Elixir&#8217;s new system, a &#8220;strong arrow&#8221; is a function or operator that the compiler knows will crash if given input outside its domain.</p><p>Examples:</p><ul><li><p><code>String.upcase/1</code> is a strong arrow (crashes on non-strings).</p></li><li><p>The <code>+</code> operator is a strong arrow (crashes on non-numbers).</p></li></ul><h3><strong>The Superpower: Backwards Inference</strong></h3><p>When the compiler sees <code>x + 1</code>, it infers backwards that <code>x</code> must be a number. You don&#8217;t need to annotate <code>x</code>; the usage dictates the type.</p><p><strong>Takeaway</strong>: The more strong arrows you use (via guards, pattern matching, and kernel functions), the more the compiler learns about your code automatically.</p><div><hr></div><h2><strong>Evolution of the Type System (v1.17 &#8594; v1.20)</strong></h2><h3><strong>Elixir 1.17: Detecting Logical Fallacies</strong></h3><p>This release introduced the set-theoretic core. Its biggest impact was detecting impossible comparisons derived from pattern matching.</p><p><strong>Example</strong>: The compiler spots when a variable constrained by a match is compared against a disjoint type.</p><pre><code><code>5 &gt; "hello"</code></code></pre><p>It also emits warnings when you do structural comparison between structs. The most common cases are comparing <code>Date</code> and <code>DateTime</code>.</p><pre><code><code>my_date &lt; ~D[2010-04-17]</code></code></pre><h3><strong>Elixir 1.18: Function Boundaries</strong></h3><p>Inference extended across function calls, alongside gradual inference of patterns and return types.</p><p><strong>The Check</strong>: The compiler can warn if you call a function with an incorrect pattern. Example: <code>User.drive({:ok, %User{}}, car_choices)</code> instead of <code>User.driver(%User{}, car_choices)</code>. It can also warn you about clauses that will never match in <code>case</code> statements.</p><p><strong>Impact</strong>: Function heads start acting as strict type filters. The compiler can detect dead code in <code>case</code> statements.</p><h3><strong>Elixir 1.19: Anonymous Functions &amp; Protocols</strong></h3><p>Type checking expanded to:</p><ul><li><p><strong>Anonymous Functions</strong>: Although they default to a <code>dynamic()</code> input<code>, </code>Example:<code> fn x -&gt; x + 1 end</code> is now inferred as:  <code>dynamic() -&gt; number()</code>, thanks to strong arrows inside the body,  the compiler infers <code>x</code> as a number inside the function and guarantees a numeric return</p></li><li><p><strong>Protocols</strong>: Interpolating a struct that doesn&#8217;t implement <code>String.Chars</code> into a string (<code>"#{user}"</code>) now warns at compile time.</p></li></ul><h3><strong>Elixir 1.20: The &#8220;Completeness&#8221; Release</strong></h3><p>Currently in release candidate status, v1.20 closes the loop. It brings inference to the remaining &#8220;blind spots&#8221; of the language:</p><ul><li><p><strong>Universal Constructs</strong>: <code>receive</code>, <code>try/catch</code>, <code>with</code> and <code>for</code> comprehensions are now fully understood by the inference engine.</p></li><li><p><strong>General Maps</strong>: Previous versions focused on atom-keyed maps (struct-like). v1.20 adds support for maps with arbitrary keys and operations like <code>Map.put</code> and <code>Map.delete</code>.</p></li></ul><div><hr></div><h2><strong>How to Write Elixir That the Compiler Understands</strong></h2><p>To maximize the benefits of this system, you don&#8217;t need to learn a new syntax, but you should adopt specific idiomatic patterns.</p><h3><strong>1. Require Strict Struct Updates</strong></h3><p>One of the hard deprecations introduced in v1.19 involves the struct update syntax.</p><p><strong>The Old Way (Unsafe):</strong></p><p><strong>Elixir</strong></p><pre><code><code>def update(user, name) do
  # Warning: user is 'dynamic', compiler can't verify keys
  %User{user | name: name} 
end
</code></code></pre><p><strong>The New Idiomatic Way</strong>: You must provide "evidence" that the variable is a struct before updating it.</p><p><strong>Elixir</strong></p><pre><code><code>def update(%User{} = user, name) do
  # Compiler now KNOWS user is %User{}
  %{user | name: name}
end
</code></code></pre><p><strong>Note</strong>: Using the map update syntax <code>%{...}</code> after a match is now preferred as it removes redundancy while retaining type safety.</p><h3>2. Pattern Match Structs on Function Clauses</h3><p>Adding struct type pattern matching in function clauses will help the compiler detect calls with incorrect parameters and help you avoid typo errors when accessing struct fields.</p><pre><code>defmodule User do
  def full_name(%User{} = user) do
    # compiler will emit warnings if you access
    # an non-existing property, Example: User.neim
    "#{user.name} #{user.surname}"
  end
end

# This call will emit a warning
User.fullname({:ok, %User{})
</code></pre><h3><strong>3. Use Guards to &#8220;Narrow&#8221; Types</strong></h3><p>Treat guards as type assertions. A variable starts as <code>term()</code> (the set of all values). Every guard intersects that set.</p><p><strong>Elixir</strong></p><pre><code><code>def normalize(s) when is_binary(s) do
  # Inside this block, 's' is strictly binary().
  # Calling String.trim(s) is provably safe.
  String.trim(s)
end
</code></code></pre><p><strong>Tip</strong>: v1.20+ can infer types from complex boolean guards like <code>is_integer(x) or is_float(x)</code>.</p><h3><strong>4. Prefer Structs Over Maps</strong></h3><p>Generic maps are the &#8220;least informative&#8221; structure. They do not restrict the keys or the types of values they can contain, and therefore you need to do defensive programming to ensure they have the expected keys.</p><ul><li><p><strong>Map</strong>: Keys are unconstrained. Shape is implicit.</p></li><li><p><strong>Struct</strong>: Keys are defined. Shape is explicit. Whenever possible, use a struct. It transforms a data container into a type carrier that propagates safety guarantees throughout your system.</p></li></ul><div><hr></div><h2><strong>Should Teams Still Use Dialyzer?</strong></h2><p>Yes, but its role has shifted.</p><p>Dialyzer uses Success Typing and excels at cross-module and library boundary checks, enforcing <code>@spec</code> contracts and catching inconsistencies the compiler doesn&#8217;t see.</p><p>The compiler&#8217;s new type system is sound and flow-sensitive, catching local logic errors, unreachable code, and invalid assumptions in real time.</p><p>Together, they complement each other: the compiler ensures local correctness, while Dialyzer validates global contracts. Teams should continue writing <code>@spec</code>s to retain these benefits and ease future transitions to compiler-enforced signatures.</p><div><hr></div><h2><strong>The Road Ahead (Next ~15 Months)</strong></h2><p>The &#8220;Inference Era&#8221; concludes with v1.20. The next phase involves explicit type features:</p><ul><li><p><strong>Inference across clauses and dependencies</strong> (RC3 - May 2026): Function calls into external libraries (like Phoenix or Ecto) will be type-checked.</p></li><li><p><strong>Typed Structs</strong> (v1.21+): A way to define the types of struct fields natively (e.g., <code>defstruct name: string()</code>).</p></li><li><p><strong>Type Signatures</strong>: Eventually, we will get a syntax to express function contracts that the compiler enforces, replacing <code>@spec</code> for pure static analysis.</p></li></ul><h2><strong>Closing Thoughts</strong></h2><p>Elixir is not becoming a rigid, statically typed language. It is becoming a language where idiomatic code is safe code.</p><p>By writing clear Elixir: matching on structs, using guards, and relying on standard library functions, you enable the compiler to act as a second pair of eyes. It validates your assumptions as you type, catching logical errors early and reducing the class of bugs that would otherwise surface in production.</p><p>This shift fundamentally changes how we trust and evolve Elixir systems. Large codebases become easier to refactor, APIs become more self-documenting, and correctness improves without sacrificing the language&#8217;s flexibility or ergonomics. Elixir is not changing its philosophy, it is finally giving the compiler enough information to enforce it.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.arctarus.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Gabriel Ortu&#241;o Notes! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Elixir’s Tooling: What You Get Out of the Box]]></title><description><![CDATA[A practical tour of the core tools, developer experience, and the trade-offs shaped by the BEAM.]]></description><link>https://www.arctarus.com/p/elixirs-tooling-what-you-get-out</link><guid isPermaLink="false">https://www.arctarus.com/p/elixirs-tooling-what-you-get-out</guid><dc:creator><![CDATA[Gabriel Ortuño]]></dc:creator><pubDate>Fri, 09 Jan 2026 14:10:05 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!adaL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13c8cc67-922e-4024-99a8-a99a81163609_1024x608.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!adaL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13c8cc67-922e-4024-99a8-a99a81163609_1024x608.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!adaL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13c8cc67-922e-4024-99a8-a99a81163609_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!adaL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13c8cc67-922e-4024-99a8-a99a81163609_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!adaL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13c8cc67-922e-4024-99a8-a99a81163609_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!adaL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13c8cc67-922e-4024-99a8-a99a81163609_1024x608.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!adaL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13c8cc67-922e-4024-99a8-a99a81163609_1024x608.png" width="1024" height="608" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/13c8cc67-922e-4024-99a8-a99a81163609_1024x608.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:608,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!adaL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13c8cc67-922e-4024-99a8-a99a81163609_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!adaL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13c8cc67-922e-4024-99a8-a99a81163609_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!adaL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13c8cc67-922e-4024-99a8-a99a81163609_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!adaL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13c8cc67-922e-4024-99a8-a99a81163609_1024x608.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><em>Elixir&#8217;s tooling is designed as a small, cohesive set of tools meant to be understood, composed, and trusted over time.</em></figcaption></figure></div><p>If you are new to the Elixir ecosystem, or evaluating it for your next project, you may wonder what tooling exists for the tasks every team needs: building, testing, debugging, documentation, dependency management, and observability. You may also wonder how mature these tools are in practice.</p><h2>Why tooling matters</h2><p>Tooling is a key factor when evaluating a programming language. Beyond syntax or performance, it determines how efficiently teams can build, test, debug, and operate systems over time. Today, a language is expected to provide more than a compiler: formatting, testing, documentation, dependency management, and observability are part of the baseline.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.arctarus.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Gabriel Ortu&#241;o Notes! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>In Elixir, tooling is a deliberate part of the language design and is strongly influenced by the runtime execution model. Elixir runs on a virtual machine built for massive concurrency, fault isolation, and long&#8209;running systems. This model does not align well with step&#8209;through debugging assumptions common in other ecosystems. As a result, Elixir tooling tends to prioritize observability and runtime safety over developer convenience.</p><p>Rather than relying on a large collection of third&#8209;party tools, Elixir provides a small but deeply integrated core toolset. Tools like <code>mix</code>, <code>iex</code>, <code>ExUnit</code>, and <code>Logger</code> work together out of the box and establish consistent conventions across projects. These choices come with explicit trade&#8209;offs, but they scale well for concurrent, long&#8209;running applications.</p><p>In short, Elixir&#8217;s tooling does not try to hide complexity; it makes the important complexity visible.</p><h2>Elixir core tooling (included by default)</h2><p>The following tools are included by default and cover most workflows required to build and operate Elixir applications.</p><h3>Build tool</h3><p><a href="https://hexdocs.pm/mix">Mix</a> is Elixir&#8217;s build tool and the backbone of the development workflow. It is used to create new projects, manage dependencies, compile code, run applications and tests, and build releases. Rather than delegating these responsibilities to separate tools, Mix provides a single, consistent interface for the entire lifecycle of an Elixir project.</p><p>Mix is extensible through custom tasks, allowing both applications and libraries to integrate their own workflows directly into the build system. This extensibility is widely used across the ecosystem and contributes to a uniform developer experience.</p><p>Mix also includes built&#8209;in tooling such as <code>mix format</code>, which enforces a standard code style across projects. The formatter is extensible, enabling libraries to define additional rules while preserving a shared baseline, reducing stylistic debates and improving code readability.</p><h3>REPL</h3><p><a href="https://hexdocs.pm/iex">IEx</a> is Elixir&#8217;s interactive shell and a central part of the development workflow. It allows developers to execute Elixir code interactively, explore APIs, and inspect runtime behavior.</p><p>When started with <code>iex -S mix</code>, it loads the current project and its dependencies, making it useful not only for experimentation but also for debugging and inspection in real applications. IEx provides a rich set of helpers, such as <code>h/1</code> to display documentation for modules and functions, and <code>i/1</code> to inspect data types and structures.</p><p>By integrating documentation, introspection, and runtime evaluation, IEx encourages an exploratory style of development and short feedback loops.</p><h3>Testing</h3><p><a href="https://hexdocs.pm/ex_unit">ExUnit</a> is the unit testing library included by default in Elixir. It can run tests in parallel using the runtime&#8217;s native concurrency, which can significantly improve test suite performance. This parallelism, however, requires tests to be isolated and free of shared state in order to avoid flaky behavior.</p><p>At first glance, ExUnit appears simple, but it provides a powerful and flexible set of features that scale well as projects grow:</p><ul><li><p><strong>Documentation tests</strong>: Tests can be defined directly in module documentation to ensure that code examples remain correct and up to date.</p></li><li><p><strong>Tags for modules and tests</strong>: Tags allow filtering which tests are executed, enabling workflows such as running only slow, integration, or focused test subsets.</p></li><li><p><strong>Parameterized tests</strong>: The same test logic can be executed with different inputs, although this feature currently operates at the module level.</p></li><li><p><strong>Log capture</strong>: Logs can be captured and asserted against, which is particularly useful when testing error cases or observable behavior.</p></li><li><p><strong>Temporary directories</strong>: Tests tagged with <code>:tmp_dir</code> automatically receive an isolated temporary directory, simplifying the testing of file system interactions.</p></li></ul><p>Overall, ExUnit encourages a testing style based on isolation, determinism, and fast feedback.</p><h3>Logging</h3><p>Elixir includes a built&#8209;in logging system, <a href="https://hexdocs.pm/logger">Logger</a>, designed for highly concurrent, long&#8209;running systems where logging must never become a source of instability.</p><p>Logger uses an asynchronous architecture, ensuring that application code does not block on I/O operations. It also applies back&#8209;pressure when handling log writes, preventing excessive memory usage and avoiding system crashes under high load.</p><p>Logger provides several features that support production&#8209;grade observability:</p><ul><li><p><strong>Standard log levels and compile&#8209;time purging</strong>: Lower&#8209;level logs can be removed at compile time to reduce runtime overhead.</p></li><li><p><strong>Process&#8209;based metadata</strong>: Metadata can be attached at the process level, making it easier to correlate logs with requests, jobs, or background work.</p></li><li><p><strong>Pluggable backends</strong>: Log generation is decoupled from log output, allowing logs to be written to the console, files, or external systems.</p></li><li><p><strong>Structured logging</strong>: Log formats can be customized, including structured formats like JSON.</p></li><li><p><strong>Runtime configuration</strong>: Logging behavior can be adjusted at runtime without redeploying the application.</p></li></ul><h3>Documentation</h3><p>In Elixir, <a href="https://hexdocs.pm/elixir/writing-documentation.html">documentation is treated as a first&#8209;class citizen</a>. Modules and functions can be documented directly in the source code using attributes such as <code>@moduledoc</code> and <code>@doc</code>, making documentation part of the public API rather than an afterthought.</p><p>Documentation is immediately accessible from the interactive shell through helpers like <code>h/1</code>, encouraging developers to explore and understand code from within <code>iex</code>.</p><p>Elixir&#8217;s documentation ecosystem is supported by <a href="https://github.com/elixir-lang/ex_doc">ExDocs</a>, a documentation generator capable of producing static websites from project documentation. ExDocs supports Markdown and Mermaid diagrams, enabling rich explanations to live alongside the code.</p><h3>Templating</h3><p><a href="https://hexdocs.pm/eex">EEx</a> is Elixir&#8217;s templating engine and a foundational building block for rendering content. Rather than interpreting templates at runtime, EEx compiles templates into Elixir bytecode, improving performance and allowing template errors to be detected earlier.</p><p>EEx is designed to be extended through custom engines. A notable example is Phoenix&#8217;s <code>heex</code> engine, which builds on EEx to provide safer HTML rendering and additional compile&#8209;time checks. This approach enables strong correctness guarantees while keeping templating tightly integrated with the language.</p><h3>Package management</h3><p>Elixir uses <a href="https://hex.pm">Hex</a> as its package management system, shared across Erlang, Elixir, and Gleam. Libraries are typically published to Hex and declared in the <code>mix.exs</code> project configuration file, where versions and constraints are explicitly defined.</p><p>Dependency resolution is tightly integrated with Mix, producing a lockfile that ensures deterministic and reproducible builds across environments. Dependencies can also be fetched directly from GitHub or GitLab when needed.</p><h2>Developer experience</h2><h3>Language Server</h3><p>For some time, Elixir had multiple language server implementations, which led to a fragmented developer experience. To address this, an <a href="https://elixir-lang.org/blog/2024/08/15/welcome-elixir-language-server-team/">official language server team was formed in 2024</a> with the goal of unifying these efforts. The result was a new language server, released in 2025 under the name <a href="https://expert-lsp.org">Expert</a>.</p><p>Expert combines strengths from previous implementations and includes native integration with tools such as Credo and Dialyzer. At the time of writing, it does not yet support debugging with breakpoints, which remains available in ElixirLS. As a result, Expert represents the long&#8209;term direction for Elixir&#8217;s language tooling, while ElixirLS may still be preferred by developers who rely heavily on debugger support.</p><h3>Debugging</h3><p>Elixir provides several <a href="https://hexdocs.pm/elixir/debugging.html">debugging tools</a> by default, but its approach differs from traditional step&#8209;through debugging. Debugging is primarily based on inspection, tracing, and observability rather than pausing execution and stepping through code line by line.</p><p>For day&#8209;to&#8209;day debugging, Elixir favors explicit inspection through tools such as <code>IO.inspect/2</code> and <code>dbg/2</code>. For interactive debugging, IEx supports mechanisms like Pry and breakpoints. For system&#8209;level inspection and performance analysis, tools such as Observer and built&#8209;in profiling tasks are commonly used.</p><p>This approach emphasizes understanding system behavior through observation and instrumentation, which scales better for concurrent, long&#8209;running applications.</p><h2>Advanced and optional tools</h2><h3>Static analysis</h3><p><strong>Dialyzer</strong> is a static analysis tool for Elixir and Erlang that helps detect type inconsistencies, unreachable code, and certain classes of bugs by analyzing function signatures and <a href="https://hexdocs.pm/elixir/typespecs.html">typespecs</a>. It is typically used via <a href="https://github.com/jeremyjh/dialyxir">Dialyxir</a>, which integrates it into the Mix workflow.</p><p>Dialyzer is optimistic: it only reports issues when it can prove that the code will fail at runtime. This results in no false positives, at the cost of potential false negatives. While error messages can be cryptic and the initial analysis slow, Dialyzer provides a valuable safety net in large or long&#8209;lived codebases.</p><p><strong><a href="https://github.com/rrrene/credo">Credo</a></strong> focuses on promoting good practices and consistency rather than runtime correctness. Its rules cover areas such as refactoring opportunities, software design, readability, and consistency. Credo emphasizes education by explaining the reasoning behind each rule, making it particularly useful for teams and less experienced developers.</p><p><strong><a href="https://github.com/nccgroup/sobelow">Sobelow</a></strong> is a static analysis tool designed to detect common security vulnerabilities in Phoenix web applications, such as XSS, SQL injection, and CSRF. It is framework&#8209;aware and most effective as a preventive measure during development and code review.</p><h3>Benchmarking</h3><p><a href="https://github.com/bencheeorg/benchee">Benchee</a> is the standard benchmarking tool in the Elixir ecosystem. It enables statistically meaningful comparisons between different implementations by accounting for variability and measuring execution time and memory usage.</p><p>Benchmark results can be exported in formats such as HTML, JSON, or CSV, making it easier to share findings or track performance changes over time.</p><h2>Maturity and trade&#8209;offs</h2><p>The Elixir tooling ecosystem is mature, not because it offers exhaustive automation, but because it prioritizes stability, cohesion, and operational correctness.</p><p>Core tooling is deeply integrated and evolves conservatively, favoring backward compatibility. Dependency management is deterministic and predictable. Observability and debugging tools are designed around concurrency and live systems.</p><p>Other areas are still evolving. Language tooling continues to improve, refactoring automation remains limited, and typing support is progressing gradually. Experimental work is also exploring new ideas, such as structured LLM&#8209;assisted development, though these efforts are not yet part of the standard tooling stack.</p><p>These characteristics reflect deliberate trade&#8209;offs: observability over step&#8209;through debugging, soundness over completeness, cohesion over customization, and operational focus over IDE&#8209;centric workflows.</p><h2>Who this tooling model works best for</h2><p>Elixir&#8217;s tooling model is particularly well suited for teams building systems where correctness, operability, and long&#8209;term maintenance matter more than short&#8209;term developer convenience.</p><p>It tends to work best for:</p><ul><li><p><strong>Backend and distributed systems teams</strong> building APIs, event&#8209;driven services, and long&#8209;running processes.</p></li><li><p><strong>Organizations operating production systems continuously</strong>, where observability, fault tolerance, and safe runtime behavior are critical.</p></li><li><p><strong>Teams that value conventions and shared workflows</strong> over highly customized setups, reducing cognitive load and decision fatigue.</p></li><li><p><strong>Codebases expected to evolve over years</strong>, where conservative tooling evolution and backward compatibility are advantages rather than constraints.</p></li></ul><p>Teams that rely heavily on rich IDE automation, extensive refactoring tools, or step&#8209;by&#8209;step debugging may find Elixir&#8217;s tooling model less familiar. In those cases, the emphasis on inspection, testing, and observability requires a shift in habits rather than additional tooling.</p><h2>Conclusion</h2><p>Elixir&#8217;s tooling stands out not because it tries to do everything, but because it is intentionally cohesive. The core tools cover the full development lifecycle with minimal setup, clear conventions, and strong integration with the runtime model.</p><p>Its limitations&#8212;particularly around IDE ergonomics and automated refactoring&#8212;are real, but they are the result of deliberate trade&#8209;offs rather than neglect. Elixir favors explicit behavior, operational clarity, and runtime safety, even when that means asking more from developers upfront.</p><p>For teams building reliable, long&#8209;running systems, this tooling model can be a strength rather than a weakness. Evaluating Elixir effectively means understanding not only what its tools provide, but also the philosophy that shapes them.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.arctarus.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Gabriel Ortu&#241;o Notes! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[A philosophy of software design]]></title><description><![CDATA[Techniques to reduce the complexity on software]]></description><link>https://www.arctarus.com/p/nature-of-software-complexity</link><guid isPermaLink="false">https://www.arctarus.com/p/nature-of-software-complexity</guid><dc:creator><![CDATA[Gabriel Ortuño]]></dc:creator><pubDate>Fri, 26 Dec 2025 09:55:37 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!1SUs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb713dfea-6947-4455-bdd0-ae1d515d0ec6_1024x608.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently, I was reading &#8220;A philosophy of software design&#8221;, by John Ousterhout.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1SUs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb713dfea-6947-4455-bdd0-ae1d515d0ec6_1024x608.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1SUs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb713dfea-6947-4455-bdd0-ae1d515d0ec6_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!1SUs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb713dfea-6947-4455-bdd0-ae1d515d0ec6_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!1SUs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb713dfea-6947-4455-bdd0-ae1d515d0ec6_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!1SUs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb713dfea-6947-4455-bdd0-ae1d515d0ec6_1024x608.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1SUs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb713dfea-6947-4455-bdd0-ae1d515d0ec6_1024x608.png" width="1024" height="608" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b713dfea-6947-4455-bdd0-ae1d515d0ec6_1024x608.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:608,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1SUs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb713dfea-6947-4455-bdd0-ae1d515d0ec6_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!1SUs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb713dfea-6947-4455-bdd0-ae1d515d0ec6_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!1SUs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb713dfea-6947-4455-bdd0-ae1d515d0ec6_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!1SUs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb713dfea-6947-4455-bdd0-ae1d515d0ec6_1024x608.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Spaghetti dependencies in a software system</figcaption></figure></div><p></p><h1>Nature of software complexity</h1><p>Software complexity refers to any aspect of a system&#8217;s structure that makes it difficult to understand and modify.</p><p>Complexity is determined by the activities that are most common. If some parts of a system are very complicated but are almost never touched, they have little impact on the system&#8217;s overall complexity. Therefore, isolating complexity in a place where it will rarely be seen is almost as effective as eliminating it entirely.</p><p>It is also important to remember that complexity is more apparent to readers than to writers. If a piece of code seems simple to its author but complex to other developers, then it is complex.</p><h2>Symptoms of Complexity</h2><h3>Change Amplification</h3><p>A system suffers from change amplification when a simple change requires modifications in many different places.</p><h3>Cognitive Load</h3><p>Cognitive load refers to how much a developer needs to know in order to complete a task. Some abstractions increase complexity because they require extensive background knowledge to make even small changes.</p><p>In some cases, an approach that requires more lines of code is actually simpler because it reduces cognitive load.</p><h3>Unknown Unknowns</h3><p>Unknown unknowns arise when it is not obvious which pieces of code must be modified to complete a task, or what information a developer needs. This is the most severe manifestation of complexity, as it often means that required changes are only discovered after a bug appears.</p><p>This situation sometimes depends on subtle design decisions that were never documented.</p><p>One of the most important goals of good design is to make a system obvious.</p><h2>Causes of Complexity</h2><h3>Dependencies</h3><p>A dependency exists when a piece of code cannot be understood or modified in isolation. One of the main goals of software design is to reduce the number of dependencies and to make the remaining ones as simple and obvious as possible.</p><h3>Obscurity</h3><p>Obscurity occurs when important information is not clear or visible. Common causes include:</p><ul><li><p>Important information that is not obvious, such as overly generic variable names (for example, <code>time</code>).</p></li><li><p>Inconsistencies, such as using the same variable name for different purposes.</p></li><li><p>Inadequate documentation.</p></li><li><p>Design Issues: When a system is clean and obvious, it requires less documentation. Poor design increases obscurity and, as a result, complexity.</p></li></ul><h2>Complexity Is Incremental</h2><p>Complexity grows over time through the accumulation of many dependencies and obscurities, which makes it increasingly difficult to control. To slow this growth, teams must adopt a zero-tolerance philosophy toward unnecessary complexity.</p><h1>Modules should be deep</h1><p>Modular design is design the systems so that developers only need to face a small fraction of the overall complexity at any given time.</p><h2>Modular design</h2><p>Software systems are decomposed into a collection of modules that are relatively independent. Modules must work together by calling each others&#8217;s functions or methods, which create dependencies between the modules.</p><p>To identify the dependencies we think in the modules as a interface and a implementation. The interface is everything that a developer must know to use the module. The implementation is the code that carries the promises made by the interface.</p><p>The best modules are those whose interfaces are much simpler than their implementations.</p><p>The interface to a module contains two kinds of information: formal and informal. The formal interface for a method is its signature, and the informal is the rest of the information that a developer needs to know to use a module, which are usually just described using comments.</p><h2>Abstractions</h2><p>An abstraction is a simplified view of an entity, which omits unimportant details.</p><p>Each module provides an abstraction. The interface presents a simplified view of the module&#8217;s functionality. An abstraction can go wrong in two ways:</p><ul><li><p>It can include details that are not really important, which make the abstraction more complicated that necessary.</p></li><li><p>It can omit details that are really important, this result in obscurity.</p></li></ul><p>So, the key to design abstractions is to understand what it&#8217;s important.</p><h2>Deep modules</h2><p>The best modules are those that provide powerful functionality yet have simple interfaces. A deep module is a good abstraction because only a small fraction of its internal complexity is visible to its users.</p><h2>Shallow modules</h2><p>A shallow module is one whose interface is relatively complex in comparison to the functionality that it provides, it is no simpler to think about the interface than to think about the full implementation.</p><h2>Classitis</h2><p>Usually, it&#8217;s recommended that the classes and the methods should be small, the problem of this approach is that this generate a large number of shallow classes, they are individually simple, but they add a lot of complexity to the system, and also result in a more verbose programming style.</p><blockquote><p>Interfaces should be designed to make the common case as simple as possible.</p></blockquote><h1>Information hiding (and leakage)</h1><h2>Information hiding</h2><p>One of the most important techniques for achieving deep modules is information hiding. The idea is that each module should encapsulate a few pieces of knowledge, which represents design decisions. The knowledge is embedded in the module&#8217;s implementation but doesn&#8217;t appear in its interface, so it is not visible to other modules. It usually consists of details about how to implement some mechanism.</p><p>The hidden information includes data structures and algorithms related to the mechanism. It can also include lower-level details such as the size of a page, and it can include higher-level  concepts that are more abstract, such as an assumption that most files are small.</p><p>It simplifies the interface of a module and reduces the cognitive load on developers who use the module.</p><h2>Information leakage</h2><p>Information leakage occurs when a design decision is reflected in multiple modules. This creates a dependency between the modules: any change to that design decision will require changes to all of the involved modules.</p><blockquote><p>Information leakage is one of the most important red flags in software design.</p></blockquote><h2>Temporal decomposition</h2><p>In temporal decomposition, the structure of a system corresponds to the time order in which operations will occur.</p><p>When designing modules, focus on the knowledge that&#8217;s needed to perform each tasks, not the order in which tasks occur.</p><p>Information hiding can often be improved by making a class slightly larger.</p><p>Default values of parameters illustrate the principle that interfaces should be designed to make the common case as simple as possible. They are also an example of partial information hiding.</p><h1>General-Purpose modules are deeper</h1><p>Over-specialization usually introduces complexity without providing significant additional functionality, and it is more of a tactical investment.</p><p>One of the best ways to produce a deep API is to make it general-purpose. This allows it to address a broad range of problems and typically results in better information hiding.</p><p>One of the most effective ways to simplify code is to eliminate special cases, so that the common-case code also handles edge cases.</p><p>When creating a general-purpose solution, it may include facilities that are never actually needed. The sweet spot is to implement new modules in a somewhat general-purpose manner. A module&#8217;s functionality should reflect current needs, but its interface should not. The interface should be general enough to support multiple use cases.</p><p>Over-specialization cannot be completely eliminated, but it should be pushed upward and downward, into the application and infrastructure layers.</p><p>One of the most important aspects of software design is determining who needs to know what, and when. When details are important, it is better to make them explicit and as obvious as possible.</p><h1>Different Layer, Different Abstraction</h1><p>Software system are composed of layer, where higher layers use the facilities provided by lower layers. Each layer provides a different abstraction from the layers above and below it. If adjacent layers have similar abstractions, this is a red flag that suggest a problem with the class decomposition.</p><h2>Pass-through methods</h2><p>A pass-through method is one that just invoke another method with the same API. This indicates a not clean division of responsibilities and make the classes shallower. Introduce complexity but don&#8217;t add functionality to the system. The solution is to refactor the classes so that each class has a distinct and coherent set of responsabilities.</p><h2>When is interface duplication OK?</h2><p>Having methods with the same signature is not always bad. The imporant thing is that each new method should contribute significant functionality. One example is a dispatcher. A dispatcher is a method that uses its arguments to select one of several other methods to invoke.</p><h2>Decorators</h2><p>The decorator design pattern encourages API duplication across layers. The motivation is separate special-purpose extensions of a class from a generic core. Decorators tend to be shallow. Before creating a decorator consider the following alternatives:</p><ul><li><p>Add the new functionality directly to the underlying class.</p></li><li><p>Merge it with the use case.</p></li><li><p>Merge with an already existing decorator.</p></li><li><p>Could be implemented as stand-alone?</p></li><li><p>Is it a wrapper to translated a external class whose interface cannot be modified, but it must conform a different interface?</p></li></ul><h2>Interface versus implementation</h2><p>The interface of a class should normally be different from its implementation: the representations used internally should be different from the abstractions that appear in the interface.</p><h2>Pass-through variables</h2><p>Another form of API duplication across layers is a pass-through variable, which is a variable that is passed down through a long chain of methods. They add complexity because they force all of the intermediate methods to be aware of their existence, even though the methods have no use for the variables. </p><p>One way of solve this is introduce a context object which stores all of the application&#8217;s global state. It will probably be needed in many places, so it can potentially become a pass-through variable. Without discipline, a context can turn into a huge grab-bag of data that creates non obvious dependencies throughout the system. They may also have thread-safe issues, so the best option is that variables of a context should be inmutable.</p><h1>Pull complexity downwards</h1><p>Most modules have more users than developers, so it is better for the developers to suffer than the users, what means than it is more important for a module to have a simple interface than a simple implementation.</p><p>Configuration parameters provide an easy excuse to avoid dealing with important issues and pass them on to someone else. When you do create configuration parameters, see if you can provide reasonable defaults, so users will only need to provide values under exceptional conditions.</p><h2>Taking it too far</h2><p>An extreme approach would be to pull all of the functionality of the entire application down into a single class, what clearly doesn&#8217;t make sense. You should pull down complexity if:</p><ul><li><p>It is closely related to the class&#8217;s existing functionality.</p></li><li><p>It will result in simplification elsewhere in the application.</p></li><li><p>Simplifies the class&#8217;s interface.</p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.arctarus.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Gabriel Ortu&#241;o Notes! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[The best code you can write]]></title><description><![CDATA[Code is read many more times than it is written, and its ultimate cost is often very high and paid by someone else.]]></description><link>https://www.arctarus.com/p/the-best-code-you-can-write</link><guid isPermaLink="false">https://www.arctarus.com/p/the-best-code-you-can-write</guid><dc:creator><![CDATA[Gabriel Ortuño]]></dc:creator><pubDate>Sun, 03 May 2020 21:22:09 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!SAJ3!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F700f9bc1-87e8-44f9-8972-c68f9fb526ea_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Code is read many more times than it is written, and its ultimate cost is often very high and paid by someone else.</p><p>While it&#8217;s difficult to get exact figures for value and cost, asking the following questions will give you insight into the potential expense of a bit of code:</p><p>How difficult was it to write? How hard is it to understand? How expensive will it be to change?</p>]]></content:encoded></item><item><title><![CDATA[DRY]]></title><description><![CDATA[Don&#8217;t repeat yourself is a principle of software development aimed at reducing repetition of code and replace it with abstractions.]]></description><link>https://www.arctarus.com/p/dry</link><guid isPermaLink="false">https://www.arctarus.com/p/dry</guid><dc:creator><![CDATA[Gabriel Ortuño]]></dc:creator><pubDate>Thu, 23 Apr 2020 12:27:24 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!SAJ3!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F700f9bc1-87e8-44f9-8972-c68f9fb526ea_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Don&#8217;t repeat yourself is a principle of software development aimed at reducing repetition of code and replace it with abstractions.</p><p>DRY principle says:</p><p>Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.</p><p>If you&#8217;ve duplicated a bit of code in many places, the DRY principle tells you to extract the duplication into a single common method and then invoke this new method in place of the old code.</p>]]></content:encoded></item><item><title><![CDATA[How to Refactor]]></title><description><![CDATA[When you have to add a new requirement to an existing code, first you should check if the code is open to the new change.]]></description><link>https://www.arctarus.com/p/how-to-refactor</link><guid isPermaLink="false">https://www.arctarus.com/p/how-to-refactor</guid><dc:creator><![CDATA[Gabriel Ortuño]]></dc:creator><pubDate>Tue, 14 Apr 2020 21:01:39 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!SAJ3!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F700f9bc1-87e8-44f9-8972-c68f9fb526ea_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When you have to add a new requirement to an existing code, first you should check if the code is open to the new change. Open means that the code is following the SOLID principle open for extension and closed for modification.</p><p>If the code isn&#8217;t open yet, you should first refactor it to make it open, and once done you can add the new code.</p><p>To refactor to make it open, you should be guided by the code smells.</p>]]></content:encoded></item><item><title><![CDATA[When to Refactor]]></title><description><![CDATA[Refactoring is not an activity you should set time aside to do.]]></description><link>https://www.arctarus.com/p/when-to-refactor</link><guid isPermaLink="false">https://www.arctarus.com/p/when-to-refactor</guid><dc:creator><![CDATA[Gabriel Ortuño]]></dc:creator><pubDate>Sun, 12 Apr 2020 15:27:40 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!SAJ3!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F700f9bc1-87e8-44f9-8972-c68f9fb526ea_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Refactoring is not an activity you should set time aside to do. You refactor because you want to do something else and refactoring helps you with that other thing.</p><p>So, when should you refactor:</p><p>The rule of three The third time you do something similar, you should refactor it.</p><p>When you add a function You want to add a feature to an existing code you have written or have been written by someone else.</p>]]></content:encoded></item><item><title><![CDATA[Refactoring]]></title><description><![CDATA[Refactoring is a technique for restructuring code in a way that you improve the design, but don&#8217;t change the its external behaviour.]]></description><link>https://www.arctarus.com/p/refactoring</link><guid isPermaLink="false">https://www.arctarus.com/p/refactoring</guid><dc:creator><![CDATA[Gabriel Ortuño]]></dc:creator><pubDate>Sat, 11 Apr 2020 21:27:48 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!_Fox!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341c4db2-aba2-47d9-8f22-6dd9379476a5_1024x608.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_Fox!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341c4db2-aba2-47d9-8f22-6dd9379476a5_1024x608.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_Fox!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341c4db2-aba2-47d9-8f22-6dd9379476a5_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!_Fox!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341c4db2-aba2-47d9-8f22-6dd9379476a5_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!_Fox!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341c4db2-aba2-47d9-8f22-6dd9379476a5_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!_Fox!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341c4db2-aba2-47d9-8f22-6dd9379476a5_1024x608.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_Fox!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341c4db2-aba2-47d9-8f22-6dd9379476a5_1024x608.png" width="1024" height="608" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/341c4db2-aba2-47d9-8f22-6dd9379476a5_1024x608.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:608,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_Fox!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341c4db2-aba2-47d9-8f22-6dd9379476a5_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!_Fox!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341c4db2-aba2-47d9-8f22-6dd9379476a5_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!_Fox!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341c4db2-aba2-47d9-8f22-6dd9379476a5_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!_Fox!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F341c4db2-aba2-47d9-8f22-6dd9379476a5_1024x608.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">moving blocks</figcaption></figure></div><p>Refactoring is a technique for restructuring code in a way that you improve the design, but don&#8217;t change the its external behaviour.</p><p>It is done applying a serie of small transformations to the code (called a &#8220;refactor&#8221;), and using test to ensure the external behaviour never changes.</p><p>Thanks that each refactor is small, if something goes wrong and a test fails, is easy go back, undo the changes, minimizing the probability that a bug can be introduced.</p>]]></content:encoded></item><item><title><![CDATA[Zero Bug Software Development]]></title><description><![CDATA[Zero Bug Software Development is a policy to archive an state of zero known bugs.]]></description><link>https://www.arctarus.com/p/zero-bug-software-development</link><guid isPermaLink="false">https://www.arctarus.com/p/zero-bug-software-development</guid><dc:creator><![CDATA[Gabriel Ortuño]]></dc:creator><pubDate>Tue, 07 Apr 2020 21:31:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!SbCb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928d4820-83ee-494a-9fdb-ec2b75229664_1024x608.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SbCb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928d4820-83ee-494a-9fdb-ec2b75229664_1024x608.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SbCb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928d4820-83ee-494a-9fdb-ec2b75229664_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!SbCb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928d4820-83ee-494a-9fdb-ec2b75229664_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!SbCb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928d4820-83ee-494a-9fdb-ec2b75229664_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!SbCb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928d4820-83ee-494a-9fdb-ec2b75229664_1024x608.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SbCb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928d4820-83ee-494a-9fdb-ec2b75229664_1024x608.png" width="1024" height="608" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/928d4820-83ee-494a-9fdb-ec2b75229664_1024x608.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:608,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!SbCb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928d4820-83ee-494a-9fdb-ec2b75229664_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!SbCb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928d4820-83ee-494a-9fdb-ec2b75229664_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!SbCb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928d4820-83ee-494a-9fdb-ec2b75229664_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!SbCb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928d4820-83ee-494a-9fdb-ec2b75229664_1024x608.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">zero bugs</figcaption></figure></div><p>Zero Bug Software Development is a policy to archive an state of zero known bugs. The first step to reach this state is label the issues using a very strict classification:</p><p>Critical issues: The user is not receiving the value that is supposed to receive. You should stop what you are doing and fix it inmediatelly. Bugs: The app is not working as expected but the users can receive the value that is supposed to.</p>]]></content:encoded></item><item><title><![CDATA[Flocking rules]]></title><description><![CDATA[The flocking rules are an small set of rules for refactoring code.]]></description><link>https://www.arctarus.com/p/flocking-rules</link><guid isPermaLink="false">https://www.arctarus.com/p/flocking-rules</guid><dc:creator><![CDATA[Gabriel Ortuño]]></dc:creator><pubDate>Sun, 05 Apr 2020 21:16:08 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!hX7T!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb782dc79-e129-46d7-848c-3c7df680dd0a_1024x608.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hX7T!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb782dc79-e129-46d7-848c-3c7df680dd0a_1024x608.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hX7T!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb782dc79-e129-46d7-848c-3c7df680dd0a_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!hX7T!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb782dc79-e129-46d7-848c-3c7df680dd0a_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!hX7T!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb782dc79-e129-46d7-848c-3c7df680dd0a_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!hX7T!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb782dc79-e129-46d7-848c-3c7df680dd0a_1024x608.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hX7T!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb782dc79-e129-46d7-848c-3c7df680dd0a_1024x608.png" width="1024" height="608" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b782dc79-e129-46d7-848c-3c7df680dd0a_1024x608.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:608,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hX7T!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb782dc79-e129-46d7-848c-3c7df680dd0a_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!hX7T!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb782dc79-e129-46d7-848c-3c7df680dd0a_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!hX7T!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb782dc79-e129-46d7-848c-3c7df680dd0a_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!hX7T!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb782dc79-e129-46d7-848c-3c7df680dd0a_1024x608.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">flock</figcaption></figure></div><p>The flocking rules are an small set of rules for refactoring code. The idea is make small incremental changes that allows you obtein precise error messages when something goes wrong, so if you find and error, you can revert it and make a smaller one. The steps are the following:</p><p>Select the things that are most alike. Find the smallest difference between them. Make the simplest change that will remove that difference.</p>]]></content:encoded></item><item><title><![CDATA[Build Less]]></title><description><![CDATA[So what to do then?]]></description><link>https://www.arctarus.com/p/build-less</link><guid isPermaLink="false">https://www.arctarus.com/p/build-less</guid><dc:creator><![CDATA[Gabriel Ortuño]]></dc:creator><pubDate>Fri, 03 Jun 2011 14:34:50 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!l9hr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b8c125f-2543-4869-847c-abfb47474f3e_1024x608.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!l9hr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b8c125f-2543-4869-847c-abfb47474f3e_1024x608.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!l9hr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b8c125f-2543-4869-847c-abfb47474f3e_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!l9hr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b8c125f-2543-4869-847c-abfb47474f3e_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!l9hr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b8c125f-2543-4869-847c-abfb47474f3e_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!l9hr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b8c125f-2543-4869-847c-abfb47474f3e_1024x608.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!l9hr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b8c125f-2543-4869-847c-abfb47474f3e_1024x608.png" width="1024" height="608" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3b8c125f-2543-4869-847c-abfb47474f3e_1024x608.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:608,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!l9hr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b8c125f-2543-4869-847c-abfb47474f3e_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!l9hr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b8c125f-2543-4869-847c-abfb47474f3e_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!l9hr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b8c125f-2543-4869-847c-abfb47474f3e_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!l9hr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b8c125f-2543-4869-847c-abfb47474f3e_1024x608.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">minimal house design blueboard</figcaption></figure></div><p></p><p>So what to do then? The answer is less. Do less than your competitors to beat them. Solve the simple problems and leave the hairy, difficult, nasty problems to everyone else. Instead of oneupping, try one-downing. Instead of outdoing, try underdoing.</p><p>Less features Less options/preferences Less people and corporate structure Less meetings and abstractions Less promises External links 37 Signals - Getting Real: Build Less Related notes What is refactoring.</p>]]></content:encoded></item></channel></rss>