<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Noah Clark</title><link>https://noahc.net/</link><description>Recent content on Noah Clark</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Wed, 08 Apr 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://noahc.net/index.xml" rel="self" type="application/rss+xml"/><item><title>Your First Day with Elixir: Building Something Beautiful from Scratch</title><link>https://noahc.net/series/herb-wars/01-getting-started/</link><pubDate>Sun, 01 Feb 2026 00:00:00 +0000</pubDate><guid>https://noahc.net/series/herb-wars/01-getting-started/</guid><description>&lt;p&gt;I&amp;rsquo;ve been a Ruby on Rails developer for most of my career. Over the years I&amp;rsquo;ve heard of the benefits of functional programming, but when I looked into it the syntax and approach seemed out of touch with the ergonomics that Ruby provides. Elixir helps to bridge that gap.&lt;/p&gt;
&lt;p&gt;LazyGit and LazyDocker are growing in popularity so let&amp;rsquo;s build our own TUI, but instead of wiring it up to git or docker, we&amp;rsquo;re going to build a retro game inspired by the classic Drug Wars. The first part of this tutorial is about laying the groundwork you&amp;rsquo;ll need to continue to learn elixir with me.&lt;/p&gt;</description></item><item><title>Immutability Isn't Scary: Building Your First Elixir Data Structures</title><link>https://noahc.net/series/herb-wars/02-data-structures/</link><pubDate>Sun, 08 Feb 2026 00:00:00 +0000</pubDate><guid>https://noahc.net/series/herb-wars/02-data-structures/</guid><description>&lt;p&gt;When I first heard that Elixir was &amp;ldquo;immutable,&amp;rdquo; I understood what this meant in theory. However, it took writing a bit of Elixir code to really understand what this meant. This section is focused on really getting at the heart of how to build with immutability at its core.&lt;/p&gt;
&lt;p&gt;How do you write a game where the player&amp;rsquo;s money never changes? Where the inventory is frozen in time?&lt;/p&gt;
&lt;p&gt;Today, we&amp;rsquo;re building a Player module that tracks money, inventory, and upgrades. We&amp;rsquo;ll write every test before we write the code it tests — so you&amp;rsquo;ll see the design emerge from what we &lt;em&gt;want&lt;/em&gt; the Player to do, not from implementation details.&lt;/p&gt;</description></item><item><title>Map, Filter, Reduce: The Enum Module is Your New Best Friend</title><link>https://noahc.net/series/herb-wars/03-enums-and-lists/</link><pubDate>Sun, 15 Feb 2026 00:00:00 +0000</pubDate><guid>https://noahc.net/series/herb-wars/03-enums-and-lists/</guid><description>&lt;p&gt;Coming from other languages, you might be used to writing loops. Lots of loops. &lt;code&gt;for&lt;/code&gt; loops, &lt;code&gt;while&lt;/code&gt; loops, &lt;code&gt;forEach&lt;/code&gt;. They&amp;rsquo;re everywhere.&lt;/p&gt;
&lt;p&gt;Elixir has a different philosophy. Instead of telling the computer &lt;em&gt;how&lt;/em&gt; to iterate, you describe &lt;em&gt;what&lt;/em&gt; you want to happen to each element. The &lt;code&gt;Enum&lt;/code&gt; module is where this magic lives, and mastering it will fundamentally change how you think about data processing.&lt;/p&gt;
&lt;p&gt;Today we&amp;rsquo;re building the core of our game: herb pricing, game state, and the actions that make HerbWars playable. By the end, you&amp;rsquo;ll be buying and selling herbs from IEx.&lt;/p&gt;</description></item><item><title>Organizing Your Elixir Code: Modules, Separation, and the Art of Not Making a Mess</title><link>https://noahc.net/series/herb-wars/04-modules-and-organization/</link><pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate><guid>https://noahc.net/series/herb-wars/04-modules-and-organization/</guid><description>&lt;p&gt;Your game already works. You can buy, sell, and travel from IEx. But IEx isn&amp;rsquo;t exactly a polished user experience. Players deserve a real interface — status displays, market listings, menus, and prompts.&lt;/p&gt;
&lt;p&gt;The key insight: we don&amp;rsquo;t touch the game logic. We build a UI layer &lt;em&gt;on top&lt;/em&gt; of the pure functions from ch3. This separation — game logic vs. presentation — is the foundation of maintainable code.&lt;/p&gt;
&lt;h2 id="one-module-one-responsibility"&gt;One Module, One Responsibility&lt;/h2&gt;
&lt;p&gt;Each module should do one thing well. Our project now has:&lt;/p&gt;</description></item><item><title>Recursion Without the Headache: Managing Game State in Elixir</title><link>https://noahc.net/series/herb-wars/05-recursion-and-state/</link><pubDate>Sun, 01 Mar 2026 00:00:00 +0000</pubDate><guid>https://noahc.net/series/herb-wars/05-recursion-and-state/</guid><description>&lt;p&gt;&amp;ldquo;Use recursion for your game loop.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Many programming languages support recursion, but for the vast majority of my career, I&amp;rsquo;ve avoided it. Even when it seemed intuitive, I&amp;rsquo;d get feedback from other developers that this was exotic. Elixir changes that and turns recursion into an idiomatic approach.&lt;/p&gt;
&lt;h2 id="the-game-loop-concept"&gt;The Game Loop Concept&lt;/h2&gt;
&lt;p&gt;Every game has the same basic structure:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Display current state&lt;/li&gt;
&lt;li&gt;Get player input&lt;/li&gt;
&lt;li&gt;Process action&lt;/li&gt;
&lt;li&gt;Update state&lt;/li&gt;
&lt;li&gt;Check win/lose conditions&lt;/li&gt;
&lt;li&gt;Repeat until game ends&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In languages with mutable variables, you might use a &lt;code&gt;while&lt;/code&gt; loop that modifies a &lt;code&gt;gameState&lt;/code&gt; object. In Elixir, we call a function that calls itself with updated state.&lt;/p&gt;</description></item><item><title>Time Travel Debugging: Why Immutable State is a Superpower</title><link>https://noahc.net/series/herb-wars/06-state-history-and-time-travel/</link><pubDate>Sun, 08 Mar 2026 00:00:00 +0000</pubDate><guid>https://noahc.net/series/herb-wars/06-state-history-and-time-travel/</guid><description>&lt;p&gt;In ch5, we built a recursive game loop that threads state through every turn. Each call to &lt;code&gt;game_loop/1&lt;/code&gt; receives a complete, immutable game state and produces a new one. We treated this as an implementation detail — a way to avoid mutable variables.&lt;/p&gt;
&lt;p&gt;But there&amp;rsquo;s something deeper going on. Because Elixir never mutates data, every game state that ever existed is still a valid, self-contained snapshot. If we hold onto those snapshots, we get something powerful: a complete, trustworthy history of everything that happened.&lt;/p&gt;</description></item><item><title>Game Mechanics: Random Events and Herb Effects</title><link>https://noahc.net/series/herb-wars/07-events-and-effects/</link><pubDate>Sun, 15 Mar 2026 00:00:00 +0000</pubDate><guid>https://noahc.net/series/herb-wars/07-events-and-effects/</guid><description>&lt;p&gt;Our game has buying, selling, and traveling — but every playthrough feels the same. Time to add some chaos. In this chapter we&amp;rsquo;ll build two systems: random events that trigger when you travel, and herb consumption with effects that wear off over time.&lt;/p&gt;
&lt;p&gt;Both follow the same pattern we&amp;rsquo;ve used throughout: build it with tests, keep the logic pure, wire up the UI last.&lt;/p&gt;
&lt;h2 id="updating-the-player-struct"&gt;Updating the Player Struct&lt;/h2&gt;
&lt;p&gt;Before building new systems, we need new fields on the Player struct. Open &lt;code&gt;lib/herb_wars/player.ex&lt;/code&gt; and update the struct:&lt;/p&gt;</description></item><item><title>From Hardcoded to Data-Driven: Loading YAML in Elixir</title><link>https://noahc.net/series/herb-wars/08-external-data-yaml/</link><pubDate>Sun, 22 Mar 2026 00:00:00 +0000</pubDate><guid>https://noahc.net/series/herb-wars/08-external-data-yaml/</guid><description>&lt;p&gt;Our game has events, effects, and real gameplay — but every piece of content is hardcoded in Elixir modules. Want to add a new event? Edit code, recompile, hope you didn&amp;rsquo;t break something.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a better way. By moving game content to external files, you separate &lt;em&gt;what the game contains&lt;/em&gt; from &lt;em&gt;how the game works&lt;/em&gt;. This chapter is a refactoring exercise: the game won&amp;rsquo;t gain new features, but it&amp;rsquo;ll become much easier to extend.&lt;/p&gt;</description></item><item><title>On naming events</title><link>https://noahc.net/zuihitsu/on-naming-events/</link><pubDate>Wed, 08 Apr 2026 00:00:00 +0000</pubDate><guid>https://noahc.net/zuihitsu/on-naming-events/</guid><description>&lt;p&gt;The hardest part of analytics isn&amp;rsquo;t the pipeline. It&amp;rsquo;s getting six engineers to agree that &lt;code&gt;user_signed_up&lt;/code&gt; and &lt;code&gt;user.created&lt;/code&gt; and &lt;code&gt;signup_complete&lt;/code&gt; are the same event. Name things once, in a spreadsheet, before you write a single line of capture code. The spreadsheet will outlast three refactors.&lt;/p&gt;</description></item><item><title>ETS tables at 3am</title><link>https://noahc.net/zuihitsu/ets-tables-at-3am/</link><pubDate>Sun, 22 Mar 2026 00:00:00 +0000</pubDate><guid>https://noahc.net/zuihitsu/ets-tables-at-3am/</guid><description>&lt;p&gt;Spent an hour debugging why an ETS lookup was returning an empty list. The table existed. The key existed. Turned out I was pattern-matching on the wrong arity — the tuple had four elements, my match expected three. The BEAM doesn&amp;rsquo;t lie. It just doesn&amp;rsquo;t explain.&lt;/p&gt;</description></item><item><title>Capturing PostHog Events in Rails Without the Footguns</title><link>https://noahc.net/posts/posthog-events-rails/</link><pubDate>Sun, 15 Mar 2026 00:00:00 +0000</pubDate><guid>https://noahc.net/posts/posthog-events-rails/</guid><description>&lt;div class="prose reveal"&gt;
&lt;p&gt;
 PostHog's Ruby SDK does what it promises. You call &lt;code&gt;posthog.capture&lt;/code&gt;,
 pass a distinct ID and an event name, and data arrives in your dashboard. The
 getting-started guide takes five minutes. The next six months of living with it
 take considerably longer.
&lt;/p&gt;
&lt;p&gt;
 The gap between "PostHog works" and "PostHog works well in a Rails app that
 processes mortgage applications" is where most teams lose time. Not because the
 SDK is bad — it's fine — but because the integration patterns that feel natural
 in a Rails controller are almost never the ones you want in production. I have
 spent more hours than I'd like debugging silent event loss, duplicated captures,
 and properties that looked right in development but arrived mangled in production
 because of ActiveRecord serialization quirks.
&lt;/p&gt;</description></item><item><title>Cover crops and dead code</title><link>https://noahc.net/zuihitsu/cover-crops/</link><pubDate>Tue, 10 Mar 2026 00:00:00 +0000</pubDate><guid>https://noahc.net/zuihitsu/cover-crops/</guid><description>&lt;p&gt;Crimson clover fixes nitrogen in the soil for the next season&amp;rsquo;s planting. It doesn&amp;rsquo;t produce anything you harvest directly — it improves the conditions for what comes after. Good test suites work the same way. You don&amp;rsquo;t ship the tests, but everything you ship is better because they exist.&lt;/p&gt;</description></item><item><title>ETS Beyond the Cache: Three Patterns That Survived Production</title><link>https://noahc.net/posts/ets-beyond-cache/</link><pubDate>Fri, 20 Feb 2026 00:00:00 +0000</pubDate><guid>https://noahc.net/posts/ets-beyond-cache/</guid><description>&lt;div class="prose reveal"&gt;
&lt;p&gt;Placeholder content.&lt;/p&gt;
&lt;/div&gt;</description></item><item><title>The Sidekiq queue is a promise</title><link>https://noahc.net/zuihitsu/the-sidekiq-queue/</link><pubDate>Wed, 18 Feb 2026 00:00:00 +0000</pubDate><guid>https://noahc.net/zuihitsu/the-sidekiq-queue/</guid><description>&lt;p&gt;Every job you enqueue is a promise to your future self that the work will get done. Most of the time, Sidekiq keeps that promise. But when it doesn&amp;rsquo;t — Redis restarts, memory pressure, a bad deploy — you learn very quickly which promises were load-bearing and which were decorative.&lt;/p&gt;</description></item><item><title>The Math Behind Sidekiq Retries</title><link>https://noahc.net/posts/sidekiq-retry-math/</link><pubDate>Wed, 28 Jan 2026 00:00:00 +0000</pubDate><guid>https://noahc.net/posts/sidekiq-retry-math/</guid><description>&lt;div class="prose reveal"&gt;
&lt;p&gt;Placeholder content.&lt;/p&gt;
&lt;/div&gt;</description></item><item><title>A Naming Convention for Analytics Events</title><link>https://noahc.net/posts/event-naming-conventions/</link><pubDate>Sat, 10 Jan 2026 00:00:00 +0000</pubDate><guid>https://noahc.net/posts/event-naming-conventions/</guid><description>&lt;div class="prose reveal"&gt;
&lt;p&gt;Placeholder content.&lt;/p&gt;
&lt;/div&gt;</description></item><item><title>GenServer Timeouts Are Not What You Think</title><link>https://noahc.net/posts/genserver-timeouts/</link><pubDate>Mon, 15 Dec 2025 00:00:00 +0000</pubDate><guid>https://noahc.net/posts/genserver-timeouts/</guid><description>&lt;div class="prose reveal"&gt;
&lt;p&gt;Placeholder content.&lt;/p&gt;
&lt;/div&gt;</description></item></channel></rss>