<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Software-Architecture | AkitaOnRails.com</title><link>https://akitaonrails.github.io/en/tags/software-architecture/</link><description>Fabio Akita's blog — tech, career, and assorted geek topics. English edition.</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Mon, 19 Oct 2026 23:20:52 +0000</lastBuildDate><atom:link href="https://akitaonrails.github.io/en/tags/software-architecture/index.xml" rel="self" type="application/rss+xml"/><item><title>ai-memory: Emergent Architecture and Malleable Software</title><link>https://akitaonrails.github.io/en/2026/06/14/ai-memory-emergent-architecture-malleable-software/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/06/14/ai-memory-emergent-architecture-malleable-software/</guid><pubDate>Sun, 14 Jun 2026 14:00:00 -0300</pubDate><description>&lt;p&gt;About three weeks ago I published the &lt;a href="https://akitaonrails.github.io/en/2026/05/23/i-built-memory-system-for-coding-agents-ai-memory/"&gt;post explaining ai-memory&lt;/a&gt;, my long-term memory system for coding agents. The core idea, for anyone who missed it: give Claude Code, Codex, OpenCode and the rest a shared brain that persists across sessions, a git-versioned wiki with FTS search, cross-agent handoff, and optional LLM consolidation. Every project isolated, running locally or self-hosted, without shipping your context to anybody&amp;rsquo;s cloud.&lt;/p&gt;
&lt;p&gt;So here&amp;rsquo;s the thing. In three weeks this little project grew in a way that deserves a post about the &lt;strong&gt;process&lt;/strong&gt; alone, because it became a living example of something I&amp;rsquo;ve been preaching for years: real software is grown, like a plant. Nobody builds good software piece by piece the way you snap a Lego together. Let me show you the numbers, and mostly the story they tell.&lt;/p&gt;</description><content:encoded><![CDATA[<p>About three weeks ago I published the <a href="/en/2026/05/23/i-built-memory-system-for-coding-agents-ai-memory/">post explaining ai-memory</a>, my long-term memory system for coding agents. The core idea, for anyone who missed it: give Claude Code, Codex, OpenCode and the rest a shared brain that persists across sessions, a git-versioned wiki with FTS search, cross-agent handoff, and optional LLM consolidation. Every project isolated, running locally or self-hosted, without shipping your context to anybody&rsquo;s cloud.</p>
<p>So here&rsquo;s the thing. In three weeks this little project grew in a way that deserves a post about the <strong>process</strong> alone, because it became a living example of something I&rsquo;ve been preaching for years: real software is grown, like a plant. Nobody builds good software piece by piece the way you snap a Lego together. Let me show you the numbers, and mostly the story they tell.</p>
<h2>The numbers, today<span class="hx:absolute hx:-mt-20" id="the-numbers-today"></span>
    <a href="#the-numbers-today" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>ai-memory has, in 24 days of life (from the first commit on May 21 until now):</p>
<ul>
<li><strong>482 commits</strong> on <code>main</code>, averaging 20 a day.</li>
<li><strong>26 people</strong> committed to the repo.</li>
<li><strong>10 Rust crates</strong> in the workspace.</li>
<li><strong>~62.7k lines</strong> of production Rust, plus <strong>~11k lines</strong> of tests in <code>tests/</code>, more than <strong>1,000 test functions</strong>, and 90 <code>#[cfg(test)]</code> modules scattered through the code.</li>
<li><strong>~19.7k lines</strong> of markdown documentation (<code>docs/</code>).</li>
<li><strong>70 pull requests</strong>, of which <strong>59 merged</strong> (84% acceptance).</li>
<li><strong>27 issues</strong>, <strong>100% closed</strong>.</li>
<li>We cut <code>v1.0.0</code> on June 12 and we&rsquo;re already on <code>v1.0.6</code>.</li>
</ul>
<p>Twenty-four days. Hold onto that number, because the interesting part isn&rsquo;t the final size, it&rsquo;s the way it got here.</p>
<h2>How it started: one of everything<span class="hx:absolute hx:-mt-20" id="how-it-started-one-of-everything"></span>
    <a href="#how-it-started-one-of-everything" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>ai-memory was born small and dumb on purpose. In the first two days I nailed down the whole skeleton: workspace bootstrap, MCP server, hook capture, cross-agent handoff, git wiki versioning, an LLM provider trait, embeddings. All of it running for <strong>one user, one machine, one workspace, one architecture</strong> (Linux, basically). No multi-user, no permissions, no scopes, no Windows or macOS support. It was my agent brain, on my machine, for me.</p>
<p>On day 3, when the <a href="https://github.com/akitaonrails/ai-memory"target="_blank" rel="noopener">first external contribution landed</a> (a fix from Lucas Oliveira for OpenAI-compatible provider URLs, PR #6), the project had <strong>102 commits and around 22.9k lines</strong> of source. Already the same 10 crates as today, but with a fraction of the functionality. It was a one-of-everything MVP that worked for me and nobody else.</p>
<p>And this is where most projects die, or worse, where most inexperienced programmers get it wrong.</p>
<h2>The Lego fallacy<span class="hx:absolute hx:-mt-20" id="the-lego-fallacy"></span>
    <a href="#the-lego-fallacy" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>There&rsquo;s a widespread belief, especially among people starting out, that you can design everything a piece of software <strong>will be</strong> before writing the first line. You sit down, plan the entire architecture, break it into neat little pieces with all the folders and layers and interfaces mapped out, and then build it piece by piece. When the last Lego brick clicks in, the software is done. Beautiful on paper.</p>
<p>In practice, it&rsquo;s the fastest road to rotting code. The sooner you crystallize a structure, the sooner it becomes a straitjacket. You predicted an elaborate permission system before having any users? Congrats, now you carry the weight of maintaining an abstraction nobody uses, and that&rsquo;s almost always wrong, because you designed it guessing the future instead of answering a real need. The inexperienced programmer over-engineers at the start, and no matter how much they plan, the result starts to rot fast, because they&rsquo;re solving problems that don&rsquo;t exist yet and getting wrong the ones that do.</p>
<p>I don&rsquo;t believe in software that finishes. I already wrote about this in <a href="/en/2026/03/01/software-is-never-done-4-projects-life-after-deploy-one-shot-prompt-myth/">&ldquo;Software Is Never Done&rdquo;</a>, and it connects straight to the original Agile methodologies, the real ones, before they turned into a corporate post-it ritual. Good software is a clay sculpture: malleable, always wet, that you keep adjusting and reshaping, and that never gets finished. It&rsquo;s clay you mold while it&rsquo;s being used. The Lego tower that one day clicks into place and is done is a fantasy.</p>
<h2>What the contributors built<span class="hx:absolute hx:-mt-20" id="what-the-contributors-built"></span>
    <a href="#what-the-contributors-built" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>And this is where the best part of open-sourcing it comes in. That one-of-everything MVP became a cross-platform, multi-user system because <strong>a lot of people</strong> put their hands on it. I want to thank them by name, because without these people the project would be a fraction of what it is:</p>
<p><a href="https://github.com/djalmajr"target="_blank" rel="noopener"><strong>Djalma Júnior</strong></a> drove a big chunk of the heavy architecture (multi-user, admin, cross-project recall), with 25 merged PRs. <a href="https://github.com/wblech"target="_blank" rel="noopener"><strong>wlech</strong></a> with dozens of commits. <a href="https://github.com/pedrofjr"target="_blank" rel="noopener"><strong>Pedro de Freitas Jr</strong></a> on embeddings and web fixes. <a href="https://github.com/mrpaiva"target="_blank" rel="noopener"><strong>Mauro Couto de Paiva</strong></a> and <a href="https://github.com/CaTeIM"target="_blank" rel="noopener"><strong>Gustavo Cateim</strong></a> drove a big part of the Windows support. <a href="https://github.com/pablowinck"target="_blank" rel="noopener"><strong>Pablo Winter</strong></a>, <a href="https://github.com/lucasliet"target="_blank" rel="noopener"><strong>Lucas Oliveira</strong></a> (author of the first external PR), and <strong>Said Rodrigues</strong>, among many others. Thank you so much, folks.</p>
<p>What those contributions added, the highlights:</p>
<ul>
<li><strong>Real Windows, macOS, and Linux support.</strong> What was Linux-only got macOS bash 3.2 compatibility, an NTFS file index for tracking inodes on Windows, and native per-platform hook commands.</li>
<li><strong>Frontend API (<code>/api/v1</code>) and a customizable web UI.</strong> It stopped being only MCP + CLI and got a real HTTP surface, with clickable wiki links and search by file path.</li>
<li><strong>Multi-user with attribution.</strong> A <code>users</code> table, writer/reader commands, and write attribution in the same SQL transaction as the data (<code>pages.author_id</code> + frontmatter).</li>
<li><strong>Multi-workspace and cross-project recall.</strong> Global search, links between wikis of different projects, and active-project isolation per session and per actor.</li>
<li><strong>More providers and more agents.</strong> Gemini, Copilot via OAuth, OpenAI-compat strict mode; plus support for Antigravity, Grok, and VS Code Copilot on top of the originals.</li>
<li><strong>Operational robustness.</strong> Checkpoint recovery, <code>reindex-from-wiki</code> to rebuild the index from the markdown, self-describing <code>_meta.md</code> manifests.</li>
</ul>
<p>Notice something: none of this was in the original plan. There was no original plan. Each of these features landed because someone needed it, opened an issue or a PR, and the structure gave way to make room. Multi-user wasn&rsquo;t predicted: it emerged.</p>
<h2>And only now did we stop to consolidate<span class="hx:absolute hx:-mt-20" id="and-only-now-did-we-stop-to-consolidate"></span>
    <a href="#and-only-now-did-we-stop-to-consolidate" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Here&rsquo;s the central point of the post. Over these three weeks, the code grew a little messy on purpose. When multi-user support came in, the logic for &ldquo;which workspace and project is this request aiming at&rdquo; ended up <strong>embedded right in the middle</strong> of several handlers in the MCP server, the web API, the admin, scattered and duplicated. Each spot resolved scope its own way. There was duplication. There was code that wasn&rsquo;t pretty. And that&rsquo;s fine, because it was working, and working for a lot of people.</p>
<p>Only <strong>after</strong> the software was already running on dozens of people&rsquo;s machines, across three operating systems, with real multi-user in production, did I stop and say: &ldquo;okay, now is a good time to refactor and rethink the internal wiring.&rdquo; The second-to-last big commit, <a href="https://github.com/akitaonrails/ai-memory"target="_blank" rel="noopener"><code>refactor: centralize scope auth and wiki consistency</code></a>, did exactly that, with 1,239 lines added and 249 removed, creating a central 601-line <code>ScopeResolver</code> and a consolidated <code>ActorContext</code>.</p>
<p>Look at the &ldquo;before.&rdquo; This scope-resolution snippet lived duplicated, with variations, across several methods:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// BEFORE: scope logic embedded, repeated in every handler
</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">active</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">active_project</span><span class="p">.</span><span class="n">get_for</span><span class="p">(</span><span class="n">actor</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">explicit_project</span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="kt">str</span>::<span class="n">trim</span><span class="p">).</span><span class="n">filter</span><span class="p">(</span><span class="o">|</span><span class="n">s</span><span class="o">|</span><span class="w"> </span><span class="o">!</span><span class="n">s</span><span class="p">.</span><span class="n">is_empty</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">((</span><span class="n">active_ws</span><span class="p">,</span><span class="w"> </span><span class="n">_</span><span class="p">))</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">active</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="nb">Some</span><span class="p">(</span><span class="n">pid</span><span class="p">))</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">reader</span><span class="p">.</span><span class="n">find_project</span><span class="p">(</span><span class="n">active_ws</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="p">.</span><span class="n">to_string</span><span class="p">()).</span><span class="k">await</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nb">Ok</span><span class="p">((</span><span class="n">active_ws</span><span class="p">,</span><span class="w"> </span><span class="n">pid</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="n">active</span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="o">|</span><span class="p">(</span><span class="n">ws</span><span class="p">,</span><span class="w"> </span><span class="n">_</span><span class="p">)</span><span class="o">|</span><span class="w"> </span><span class="n">ws</span><span class="p">)</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">workspace_id</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="nb">Some</span><span class="p">(</span><span class="n">pid</span><span class="p">))</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">reader</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="n">find_project</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">workspace_id</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="p">.</span><span class="n">to_string</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="k">await</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nb">Ok</span><span class="p">((</span><span class="bp">self</span><span class="p">.</span><span class="n">workspace_id</span><span class="p">,</span><span class="w"> </span><span class="n">pid</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">McpError</span>::<span class="n">internal_error</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="fm">format!</span><span class="p">(</span><span class="s">&#34;project &#39;</span><span class="si">{name}</span><span class="s">&#39; not found in the active or default workspace&#34;</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nb">None</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nb">Ok</span><span class="p">(</span><span class="n">active</span><span class="p">.</span><span class="n">unwrap_or</span><span class="p">((</span><span class="bp">self</span><span class="p">.</span><span class="n">workspace_id</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">project_id</span><span class="p">)))</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And the &ldquo;after.&rdquo; The same resolution, now delegated to a single authority in the store crate:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// AFTER: one central resolver, one call
</span></span></span><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">scope_resolver</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">ScopeResolver</span><span class="o">&lt;</span><span class="nb">&#39;_</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">ScopeResolver</span>::<span class="n">new</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">.</span><span class="n">reader</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">workspace_id</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">project_id</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">with_writer</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">.</span><span class="n">writer</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">.</span><span class="n">with_active_project</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">.</span><span class="n">active_project</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// ... and each handler becomes this:
</span></span></span><span class="line"><span class="cl"><span class="bp">self</span><span class="p">.</span><span class="n">scope_resolver</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="n">resolve_current_or_project</span><span class="p">(</span><span class="n">explicit_project</span><span class="p">,</span><span class="w"> </span><span class="n">actor</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="k">await</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="n">ai_memory_store</span>::<span class="n">ResolvedScope</span>::<span class="n">as_tuple</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="n">map_err</span><span class="p">(</span><span class="bp">Self</span>::<span class="n">scope_error</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Three different methods that each used to carry 30 lines of embedded logic, with <code>lookup_ids</code>, workspace fallback, and their own error handling, collapsed into one call each. Alongside came the sibling commits: <code>harden multi-user admin auth boundaries</code> and <code>prevent scoped fallback bleed</code>. Centralizing permissions, workspace and user scope, and wiki consistency, all in one place, with a clear rule.</p>
<p>The detail that matters: <code>ScopeResolver</code> wasn&rsquo;t invented at the start of the project by guessing there&rsquo;d be multi-user one day. It was <strong>extracted</strong> from code that already existed, already worked, and had already shown, in practice, which patterns kept repeating. The abstraction came after the usage, not before. I let the structure emerge from the code, and only once it was obvious did I consolidate it.</p>
<h2>The right order: make it work, make it right, make it fast<span class="hx:absolute hx:-mt-20" id="the-right-order-make-it-work-make-it-right-make-it-fast"></span>
    <a href="#the-right-order-make-it-work-make-it-right-make-it-fast" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>This is the summary of everything. There&rsquo;s an old saying, attributed to Kent Beck, that I carry like a mantra: <strong>&ldquo;make it work, make it right, make it fast.&rdquo;</strong> Make it work, then make it right, then make it fast. In that order. Most inexperienced programmers want to make it &ldquo;right&rdquo; and &ldquo;fast&rdquo; before making it &ldquo;work,&rdquo; and so they invent rules with no code to justify the rule.</p>
<p>ai-memory followed the right order without me even thinking much about it. First I made it work for one person, simple scope. Then I made it work one feature at a time, refactoring and hardening the code along the way, but never falling into over-engineering and never rewriting from scratch to fit a preconceived structure. I let the current structures emerge from the code that was being added. That meant living with some duplication and some so-so code for a while, until that moment hit of &ldquo;okay, now it&rsquo;s time to refactor and rethink the wiring.&rdquo; And only now, with the software mature and in use, did I establish better patterns and rules for new code. Rules backed by real code, not by guesswork.</p>
<p>KISS, &ldquo;keep it simple,&rdquo; is about not solving a problem you don&rsquo;t have yet. It has nothing to do with writing little code. It&rsquo;s about letting architecture emerge from the code, not the other way around. You don&rsquo;t design the sculpture and pour the clay into the mold. You take the clay, start shaping, and the form appears as your hands work. The mold, if you ever need one, you build later, around the form that already exists.</p>
<p>Twenty-four days, 482 commits, 26 people, and none of them followed a diagram drawn on day 1. Because there was no diagram on day 1. And that&rsquo;s exactly why it worked.</p>
<p>ai-memory is open source and keeps changing every week: <a href="https://github.com/akitaonrails/ai-memory"target="_blank" rel="noopener">github.com/akitaonrails/ai-memory</a>. Want to help shape the next piece? There&rsquo;s an open issue waiting.</p>
]]></content:encoded><category>ai-memory</category><category>software-architecture</category><category>agile</category></item><item><title>Is RAG Dead? Long Context, Grep, and the End of the Mandatory Vector DB</title><link>https://akitaonrails.github.io/en/2026/04/06/rag-is-dead-long-context/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/04/06/rag-is-dead-long-context/</guid><pubDate>Mon, 06 Apr 2026 11:00:00 -0300</pubDate><description>&lt;p&gt;This is one of those itches I can&amp;rsquo;t scratch. Back in the early LLM days, around 2022/2023, we had 4k of context on GPT 3.5, 8k if you were lucky, 32k was a luxury. To do anything with a real document you had no choice: chop the text into pieces, generate embeddings, throw them in a vector database, do similarity search, grab the top-5 chunks, and pray the right ones came back.&lt;/p&gt;</description><content:encoded><![CDATA[<p>This is one of those itches I can&rsquo;t scratch. Back in the early LLM days, around 2022/2023, we had 4k of context on GPT 3.5, 8k if you were lucky, 32k was a luxury. To do anything with a real document you had no choice: chop the text into pieces, generate embeddings, throw them in a vector database, do similarity search, grab the top-5 chunks, and pray the right ones came back.</p>
<p>Then it became an industry. Pinecone, Weaviate, Qdrant, Chroma, Milvus, pgvector, LangChain, LlamaIndex, Haystack. Tutorials everywhere, &ldquo;build your chatbot with your PDFs,&rdquo; entire consultancies feeding off this. It became the &ldquo;hello world&rdquo; of applied LLMs: document → chunk → embed → vector DB → query.</p>
<p>Today, in April 2026, Claude Opus 4.6 has 1 million tokens of context. Sonnet 4.6 too. Gemini 3.1 Pro too. GPT 5.4 has a smaller window but still in the comfortable range, in the hundreds of thousands. And some models already have experimental 2M token modes. The question that keeps nagging at me: what on earth do I need a vector stack for, to solve a problem that fits inside the model&rsquo;s window?</p>
<p>And there&rsquo;s more: vector databases have real problems nobody wants to talk about. False neighbors. Arbitrary chunking that splits a definition from its usage. Embeddings that age badly. Not to mention that when the result is wrong, you have absolutely no idea why.</p>
<p>The thesis I&rsquo;ve been chewing on is simple: in most cases, a well-aimed <code>grep</code> plus a generous context window beats a full RAG stack. It&rsquo;s cheaper, it&rsquo;s easier to maintain, and when it breaks you can actually debug it. Let&rsquo;s break this down.</p>
<h2>What the Claude Code leak showed<span class="hx:absolute hx:-mt-20" id="what-the-claude-code-leak-showed"></span>
    <a href="#what-the-claude-code-leak-showed" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Before we get into the theory, let me bring up something that happened a few days ago that backs this whole argument up. On March 31, 2026, Anthropic, by accident, published version 2.1.88 of the <code>@anthropic-ai/claude-code</code> package on npm with a nearly 60 MB source map attached, and roughly 512,000 lines of TypeScript from their internal tool leaked into the wild. I already <a href="/en/2026/03/31/claude-code-source-code-leaked-what-we-found-inside/">wrote about the incident last week</a>, with more detail on what showed up in the code.</p>
<p>The part that matters for this discussion is Claude Code&rsquo;s memory system. Instead of dumping everything into a vector DB, the architecture has three layers. There&rsquo;s a <code>MEMORY.md</code> that stays permanently loaded in context, but it doesn&rsquo;t hold any actual data: it&rsquo;s just an index of pointers, around 150 characters per line, kept under 200 lines and about 25 KB. The real facts live in &ldquo;topic files&rdquo; that get pulled on demand when the agent needs them. And the raw transcripts from previous sessions are never reloaded whole, only searched with grep, hunting for specific identifiers. No embedding. No Pinecone. Just write discipline (topic file first, index after) and lexical search. That&rsquo;s it.</p>
<p>Claude Code&rsquo;s main loop also has a tiered system for handling a context that&rsquo;s filling up. As <a href="/en/2026/03/31/claude-code-source-code-leaked-what-we-found-inside/">I detailed in the previous post</a>, there are five different context compaction strategies, with names like <code>microcompact</code> (clears old tool results based on age), <code>context collapse</code> (summarizes long stretches of conversation), and <code>autocompact</code> (which fires when the context gets close to the limit). The CLAUDE.md file, which a lot of people thought was just a convention, is first-class in the architecture: the system re-reads it at every iteration of the query.</p>
<p>What this tells me: the best coding agent on the market right now, built by the company selling the most expensive model out there, <strong>does not use a vector DB</strong>. It uses files on disk, a markdown index, lexical search, and smart compaction strategies for when the context overflows. They could&rsquo;ve slapped embeddings on top, they have the money to run whatever they wanted, and they chose not to. The reason, in my reading, is exactly what this post is arguing: to retrieve text from files you control, with generous context available, a vector DB is dead weight. Better to invest in compacting the window you already have than indexing everything into an external store.</p>
<p>There&rsquo;s a curious security detail that came along with the leak: people noticed the compaction pipeline has a vulnerability they&rsquo;re calling &ldquo;context poisoning.&rdquo; Content that looks like an instruction, coming from a file the model reads (say, a CLAUDE.md from a cloned repo), can end up being preserved by the compaction model as if it were &ldquo;user feedback,&rdquo; and the next model takes that as a real user instruction. It&rsquo;s a new attack vector. But that&rsquo;s a topic for another post.</p>
<h3>The &ldquo;Dream&rdquo; system and memory consolidation<span class="hx:absolute hx:-mt-20" id="the-dream-system-and-memory-consolidation"></span>
    <a href="#the-dream-system-and-memory-consolidation" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>But what really caught my eye for the RAG debate, which I <a href="/en/2026/03/31/claude-code-source-code-leaked-what-we-found-inside/">unpacked in detail last week</a>, is the system called <code>autoDream</code>. It&rsquo;s a forked subagent, with read-only bash access to the project, that runs in the background while you&rsquo;re not using the tool. Its job is literally to dream: to consolidate memory. The name isn&rsquo;t accidental, and the obvious analogy (which I couldn&rsquo;t resist) is the human brain consolidating memory during sleep, turning short-term experience into something more stable.</p>
<p>For a dream to actually run, three gates have to open at once: 24 hours since the last dream, at least 5 sessions since the last dream, and a consolidation lock that prevents concurrent dreams. When it fires, it goes through four phases. Orient (does an <code>ls</code> on the memory directory, reads the index). Gather (looks for new signals in logs, stale memories, transcripts). Consolidate (writes or updates the topic files, converts relative dates into absolute ones, deletes facts that have been contradicted). And Prune, the final cleanup that keeps the index under 200 lines.</p>
<p>The decision to make <code>autoDream</code> a forked subagent is the detail that matters here. It does not run in the same loop as the main agent. Why? Because memory consolidation is a noisy process. The model has to re-read old transcripts, compare them against what&rsquo;s in <code>MEMORY.md</code>, decide what stays and what goes, form hypotheses about things it saw in earlier sessions. If that ran in the main context, it would pollute the &ldquo;train of thought&rdquo; of the agent that&rsquo;s trying to help you with your current task. By forking, you keep the two separate. The main agent stays focused on what you asked for, and <code>autoDream</code> does the housekeeping in parallel, with no write permission on the project.</p>
<p>And the way it figures out what needs to be consolidated is plain old lexical search. The transcripts live as JSONL files on disk, and <code>autoDream</code> uses grep to look for new signals. Just grep, on text logs. Stop and think about that for a second. The memory consolidation of the most advanced agent in the world, built by one of the richest AI companies out there, is a forked subagent running grep on text logs. If a vector DB were the right answer for this kind of problem, Anthropic would&rsquo;ve put a vector DB in there. They didn&rsquo;t.</p>
<p>And there&rsquo;s a detail that, to me, is the buried gold of the entire leak, and it fits this argument like a glove. In <code>autoDream</code>, memory is treated as a hint. The system assumes that what&rsquo;s stored may be stale, wrong, contradicted by something that happened later, and the model has to verify before it trusts it. The vector DB pitch is the opposite of that: index everything, search by similarity, return the top-k, trust the result. Claude Code went the conservative route. Index little, search by word, return a hint, and stay skeptical until you&rsquo;ve laid eyes on the actual fact.</p>
<p>The whole strategy works in two layers. Inside a single session: generous context plus grep plus smart compaction (<code>microcompact</code>, <code>context collapse</code>, <code>autocompact</code>). Between sessions: a subagent that consolidates memory asynchronously, using grep on the transcripts and treating the result as a tip, not as truth. Embeddings and vector DBs don&rsquo;t show up in either layer. The deliberate choice was a smart reader chewing on raw text, not a dumb reader being spoonfed the top-k of an embedding.</p>
<p>The practical lesson for our debate is simple. The most advanced agents on the market are heading toward generous context, lexical search, and smart compaction, not toward classic RAG pipelines. If Anthropic, with all the infrastructure and talent they&rsquo;ve got, picked this path for Claude Code, those of us building internal applications on a fraction of that budget should at least think about going the same way.</p>
<h2>Where the story started turning<span class="hx:absolute hx:-mt-20" id="where-the-story-started-turning"></span>
    <a href="#where-the-story-started-turning" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>When the ceiling was 32k of context, retrieval was the bottleneck of the entire problem. You had to pre-filter aggressively, because anything that made it into the window was sacred space. A vector DB was the only halfway-decent way to do that semantic pre-filtering. The logic was: &ldquo;the reader (LLM) is expensive and dumb, so the retriever has to be smart and selective.&rdquo;</p>
<p>Today the equation has flipped. The reader is now the smartest one at the table, and the window grew big enough to hold an entire document. So the retriever can (and maybe should) go back to being dumb. The dumber, the better. You want high recall and low precision, and you let the model do the fine work. Grep does exactly that. So does BM25. And ripgrep flies through millions of lines without breaking a sweat.</p>
<p>And this isn&rsquo;t just my hunch. The BEIR benchmarks have shown for a while now that BM25 matches or beats a lot of dense retrievers when the domain drifts away from where the embeddings were trained. Anthropic itself published a post on <a href="https://www.anthropic.com/news/contextual-retrieval"target="_blank" rel="noopener">Contextual Retrieval</a> that basically says the same thing: a lexical signal plus an LLM&rsquo;s judgment beats pure embeddings on most knowledge tasks. And take a look at Claude Code, the tool I&rsquo;ve been using every day for 500 hours: it navigates the repo with <code>Glob</code> and <code>Grep</code>. No vector DB, no embedding, no LangChain. It works ridiculously well.</p>
<h2>The real problems with vector databases nobody advertises<span class="hx:absolute hx:-mt-20" id="the-real-problems-with-vector-databases-nobody-advertises"></span>
    <a href="#the-real-problems-with-vector-databases-nobody-advertises" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The vector DB marketing sells the dream of perfect semantic search. Reality is messier.</p>
<p>False neighbors come first. Cosine similarity rewards topical similarity, not relevance. You ask &ldquo;how do we handle authentication errors&rdquo; and the DB returns every chunk that mentions authentication. The chunk that actually answers the question may be in tenth place, or may not have been retrieved at all because the doc author wrote &ldquo;login&rdquo; instead of &ldquo;auth.&rdquo;</p>
<p>Chunking is the second one, and it&rsquo;s a disguised disaster. A 512-token window with a 64-token overlap sounds reasonable, until you realize your important table got cut in half, the function definition ended up separated from its usage, and the piece of documentation with the exact command got orphaned without the context of its section. The chunk boundary tends to land exactly where the answer was living.</p>
<p>When it fails, it fails without leaving a trace. When BM25 misses, you know why: the word isn&rsquo;t there. When a vector DB returns garbage, you get a plausible-looking wrong chunk, with no diagnostic signal at all. Good luck debugging that in production at two in the morning.</p>
<p>The index gets stale. Every document update calls for re-embedding. If you have 10,000 docs and 200 of them change per day, that turns into a batch process, monitoring, a queue, retries, embedding API costs, and an unavoidable inconsistency window between what&rsquo;s on disk and what&rsquo;s in the index. Grep has none of that. File changed? The next query already sees it.</p>
<p>And there&rsquo;s the operating cost nobody adds up. Pinecone charges per vector. Weaviate wants a cluster to maintain. pgvector saves you a new server but you still own a schema, an index, and a re-embedding pipeline. Each of those things wants engineer time, monitoring, tests, deploys. All of that to do a search that <code>rg</code> would often crack in 200ms.</p>
<h2>Comparing the complexity<span class="hx:absolute hx:-mt-20" id="comparing-the-complexity"></span>
    <a href="#comparing-the-complexity" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Look at the diagram:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/06/rag/rag-vs-grep-complexity.png" alt="Complexity: classic RAG vs grep &#43; long context"  loading="lazy" /></p>
<p>On one side, eight steps, four or five services, an external index that needs to be maintained and kept up to date. On the other, four steps, zero new infrastructure. This isn&rsquo;t a caricature: it is literally what you have to set up for each case.</p>
<p>The honest question: does the left column pay off? In 2023, yes, because the right column didn&rsquo;t exist (no LLM had a 200k window). In 2026, in most cases, it doesn&rsquo;t.</p>
<h2>Pros and cons of each side<span class="hx:absolute hx:-mt-20" id="pros-and-cons-of-each-side"></span>
    <a href="#pros-and-cons-of-each-side" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>Classic RAG (vector DB)<span class="hx:absolute hx:-mt-20" id="classic-rag-vector-db"></span>
    <a href="#classic-rag-vector-db" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><strong>For:</strong></p>
<ul>
<li>Works for huge document bases, on the order of hundreds of GB, where even <code>rg</code> won&rsquo;t cut it without prior indexing</li>
<li>Handles heavy paraphrase and cross-lingual queries (&ldquo;how do I cancel&rdquo; vs. &ldquo;subscription termination process&rdquo;) where the user&rsquo;s vocabulary doesn&rsquo;t match the document&rsquo;s</li>
<li>Works for non-textual modalities (image, audio) where grep has nothing to look at</li>
<li>Saves input tokens if you&rsquo;re tight on budget or absolute latency</li>
</ul>
<p><strong>Against:</strong></p>
<ul>
<li>Complex stack: embedding, vector DB, chunking, reranker, re-indexing pipeline</li>
<li>Opaque failures, hard to debug</li>
<li>Chunking destroys the context of tables, code, long definitions</li>
<li>Operational overhead (index, queue, monitoring, re-embedding cost)</li>
<li>The semantic search the marketing is selling rarely works the way the marketing promises</li>
</ul>
<h3>Grep + long context<span class="hx:absolute hx:-mt-20" id="grep--long-context"></span>
    <a href="#grep--long-context" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><strong>For:</strong></p>
<ul>
<li>Practically zero new infrastructure: ripgrep, sqlite, or a plain <code>LIKE</code> in Postgres</li>
<li>Always fresh: file changes, the next query sees them</li>
<li>Transparent failures: the word is either there or it isn&rsquo;t</li>
<li>Loads the document in generous chunks, the model does the fine filtering with actual semantics</li>
<li>Cheaper in dev and ops, cheaper to pivot domains</li>
</ul>
<p><strong>Against:</strong></p>
<ul>
<li>Doesn&rsquo;t scale to terabytes of raw text without some kind of indexing</li>
<li>Suffers when the user&rsquo;s vocabulary is very different from the document&rsquo;s</li>
<li>Doesn&rsquo;t work for non-textual modalities</li>
<li>Per-query latency is higher in absolute terms (loading 100k tokens always costs more than loading 5k)</li>
<li>Per-query input cost is higher if you don&rsquo;t have prompt caching</li>
</ul>
<h2>But what about cost?<span class="hx:absolute hx:-mt-20" id="but-what-about-cost"></span>
    <a href="#but-what-about-cost" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>This is the argument I get hit with the most when I defend the &ldquo;load everything into context&rdquo; thesis. &ldquo;It&rsquo;ll get crazy expensive, 200k tokens of input per query is absurd.&rdquo; Let&rsquo;s actually run the numbers.</p>
<p>In <a href="/en/2026/04/05/testing-llms-open-source-and-commercial-can-anyone-beat-claude-opus/">yesterday&rsquo;s LLM benchmark post</a> I mapped out the per-token price of every model. Take Claude Sonnet 4.6: $3 per million input tokens, $15 per million output. Take GLM 5 (which I proved actually works): $0.60 input, $2.20 output. Take GPT 5.4 Pro at the top of the heap: $15 input, $180 output (yeah, that one stings, I know).</p>
<p>Before we turn &ldquo;200k tokens&rdquo; into dollars, let&rsquo;s land that number on something tangible, because &ldquo;100k tokens&rdquo; doesn&rsquo;t mean anything to anyone. A token, on average, is roughly 0.75 of a word in English (Portuguese is similar, maybe a touch heavier because of longer words). So, translating:</p>
<ul>
<li><strong>100k tokens</strong> ≈ 75,000 words ≈ a whole short novel like Hemingway&rsquo;s <em>The Old Man and the Sea</em> with room to spare, or about three long Wikipedia articles glued together.</li>
<li><strong>200k tokens</strong> ≈ 150,000 words ≈ a big novel, like <em>Crime and Punishment</em> in full, or half of the first <em>Game of Thrones</em> book (which clocks in around 298k words, so roughly 400k tokens).</li>
<li><strong>400k tokens</strong> ≈ 300,000 words ≈ <em>A Game of Thrones</em> in full, the entire first book of the series in your window.</li>
<li><strong>1M tokens</strong> ≈ 750,000 words ≈ the entire <em>Lord of the Rings</em> trilogy plus <em>The Hobbit</em>, or the whole Bible (King James is around 783k words, roughly 1M tokens), or about two and a half <em>Game of Thrones</em> books stacked on top of each other.</li>
</ul>
<p>So when I say &ldquo;throw 200k tokens of input at the model,&rdquo; what that actually means in the real world is &ldquo;throw the entire <em>Crime and Punishment</em> in as the context for your question.&rdquo; That&rsquo;s a lot. And that&rsquo;s exactly what makes the argument of this post viable: today&rsquo;s models can read an entire novel in one go and still answer a specific question about it. In 2023, this was science fiction. In 2026, it&rsquo;s the base case.</p>
<p>So picture a query that throws 200k tokens of input at the model (there goes <em>Crime and Punishment</em> again) and produces 2k tokens of output (about three pages of response):</p>
<table>
  <thead>
      <tr>
          <th>Model</th>
          <th style="text-align: right">Input ($)</th>
          <th style="text-align: right">Output ($)</th>
          <th style="text-align: right">Total per query</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Claude Sonnet 4.6</td>
          <td style="text-align: right">$0.60</td>
          <td style="text-align: right">$0.03</td>
          <td style="text-align: right"><strong>$0.63</strong></td>
      </tr>
      <tr>
          <td>Claude Opus 4.6</td>
          <td style="text-align: right">$3.00</td>
          <td style="text-align: right">$0.15</td>
          <td style="text-align: right"><strong>$3.15</strong></td>
      </tr>
      <tr>
          <td>GLM 5</td>
          <td style="text-align: right">$0.12</td>
          <td style="text-align: right">$0.0044</td>
          <td style="text-align: right"><strong>$0.12</strong></td>
      </tr>
      <tr>
          <td>Gemini 3.1 Pro</td>
          <td style="text-align: right">$0.40</td>
          <td style="text-align: right">$0.024</td>
          <td style="text-align: right"><strong>$0.42</strong></td>
      </tr>
      <tr>
          <td>GPT 5.4 Pro</td>
          <td style="text-align: right">$3.00</td>
          <td style="text-align: right">$0.36</td>
          <td style="text-align: right"><strong>$3.36</strong></td>
      </tr>
  </tbody>
</table>
<p>Now throw prompt caching into the mix. Claude has a cache that drops cached input to a fraction of the full price (in the ballpark of 10%, depending on the model). Gemini has a similar mechanism. When you fire a sequence of queries against the same 200k-token dump, the cost of subsequent queries plummets to pennies. With Sonnet cached, you can fairly call it about $0.10 per follow-up query without making things up.</p>
<p>Now compare that to the cost of running a Pinecone, or a Weaviate, or a pgvector. Setting aside the price of the subscription itself (which varies a lot), you need an engineer to wire up the pipeline, maintain it, monitor it, deal with embedding failures, redo the chunking when the domain shifts. Conservatively, you&rsquo;re looking at somewhere between 40 and 80 hours of engineering to make the thing stable. At R$ 200/hour, that&rsquo;s between R$ 8,000 and R$ 16,000. In USD, somewhere between $1,600 and $3,200 just to stand it up.</p>
<p>With $3,200, on Sonnet 4.6 with prompt caching, you can run something on the order of 30,000 queries of 200k tokens each. Thirty thousand queries, depending on the scale of the project, gives you several months or even an entire year of an average internal tool. And you didn&rsquo;t pay an engineer to wire up a pipeline. There&rsquo;s no vector DB server to maintain. And if the document changes, the system already sees it on the next query.</p>
<p>The &ldquo;RAG is cheaper in tokens&rdquo; argument ignores that tokens are the cheapest thing in the entire equation. Engineers cost a lot, servers cost a lot, bugs in production cost a whole lot more. Tokens have become a commodity, and they&rsquo;re getting cheaper with every new model release.</p>
<p>The classic RAG argument was &ldquo;the model is expensive, retrieval is cheap.&rdquo; Today it&rsquo;s the opposite: the model is the cheap part of the stack, smart retrieval is what costs a fortune to build and maintain.</p>
<h2>Where the thesis doesn&rsquo;t hold<span class="hx:absolute hx:-mt-20" id="where-the-thesis-doesnt-hold"></span>
    <a href="#where-the-thesis-doesnt-hold" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I don&rsquo;t want to come off as a fanboy. There are cases where classic RAG still wins:</p>
<ol>
<li><strong>Massive corpora.</strong> If you have 500 GB of raw text, even <code>rg</code> won&rsquo;t solve it in acceptable time. You need some kind of indexing. It can be indexed BM25 (Tantivy, Elasticsearch), it can be a vector DB. But notice: the first option is still lexical, not vector.</li>
<li><strong>Wildly scattered vocabulary.</strong> Customer support, where the user types &ldquo;my wifi&rsquo;s down&rdquo; and the documentation says &ldquo;loss of connectivity at the physical layer.&rdquo; BM25 won&rsquo;t catch that. Embedding will. Vector DB scores a point here.</li>
<li><strong>Non-textual modalities.</strong> Image-by-image search, audio-by-audio. Embedding is mandatory.</li>
<li><strong>Critical absolute latency.</strong> If you have to answer in 100ms with a 5k input budget, a generous dump won&rsquo;t fit. Pre-filtering is necessary.</li>
<li><strong>Compliance and audit.</strong> If you have to prove that a specific document was consulted to answer a specific query, having indexed and trackable chunks helps. A 200k-token context dump is more opaque from an audit standpoint.</li>
</ol>
<p>For those cases, classic RAG still makes sense. But notice the size of the list. These are specific cases. The general case, things like &ldquo;chat with our internal docs&rdquo; or &ldquo;ask the product manual,&rdquo; almost all of it falls into the &ldquo;grep + long context handles it better&rdquo; bucket.</p>
<h2>Lazy retrieval: the recipe I&rsquo;d defend<span class="hx:absolute hx:-mt-20" id="lazy-retrieval-the-recipe-id-defend"></span>
    <a href="#lazy-retrieval-the-recipe-id-defend" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>If I were building a &ldquo;chat with docs&rdquo; tool today, from scratch, it would look more or less like this:</p>
<ol>
<li><strong>Keep the documents raw.</strong> Markdown, converted PDF, code, whatever. On disk, organized in folders that make sense for the domain.</li>
<li><strong>Fast lexical filter.</strong> <code>ripgrep</code> with regex, or BM25 with Tantivy/SQLite FTS5, or a <code>LIKE</code> in Postgres if you already have one. Returns 100-300 hits.</li>
<li><strong>Load generously.</strong> Grab not just the matching snippet, but the entire file, or a wide window around it. Throw all of it into the context.</li>
<li><strong>Let the LLM do the fine work.</strong> Pass the original question, tell the model to find what matters, drop the rest, and answer with citations.</li>
<li><strong>(Optional) Add embeddings only for the query classes where lexical fails</strong>, after you have real data showing that it fails.</li>
</ol>
<p>This is the opposite of the old advice (&ldquo;start with vectors, fall back to keyword&rdquo;). It&rsquo;s: <strong>start with keyword, and add vector only if you feel the gap</strong>. In most projects, you never will.</p>
<h2>A toy implementation in Ruby<span class="hx:absolute hx:-mt-20" id="a-toy-implementation-in-ruby"></span>
    <a href="#a-toy-implementation-in-ruby" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>To make it concrete. Here&rsquo;s a Ruby script using the <a href="https://github.com/crmne/ruby_llm"target="_blank" rel="noopener"><code>ruby_llm</code></a> gem (the same one from yesterday&rsquo;s benchmark) that does exactly this flow: grep through the files, load the snippets with context, send to Claude, get the answer back. No vector DB, no chunking, no embedding, no LangChain.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="ch">#!/usr/bin/env ruby</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;ruby_llm&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;open3&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">DOCS_DIR</span> <span class="o">=</span> <span class="no">ARGV</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o">||</span> <span class="s2">&#34;./docs&#34;</span>
</span></span><span class="line"><span class="cl"><span class="no">QUERY</span>    <span class="o">=</span> <span class="no">ARGV</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="ow">or</span> <span class="nb">abort</span> <span class="s2">&#34;uso: ./ask.rb &lt;pasta&gt; &lt;pergunta&gt;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 1. Fast lexical filter with ripgrep.</span>
</span></span><span class="line"><span class="cl"><span class="c1">#    -i case insensitive, -l file names only, --type-add covers md/txt/extracted-pdf.</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">lexical_search</span><span class="p">(</span><span class="n">dir</span><span class="p">,</span> <span class="n">query</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">terms</span> <span class="o">=</span> <span class="n">query</span><span class="o">.</span><span class="n">downcase</span><span class="o">.</span><span class="n">scan</span><span class="p">(</span><span class="sr">/\w{4,}/</span><span class="p">)</span><span class="o">.</span><span class="n">uniq</span><span class="o">.</span><span class="n">first</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span>  <span class="c1"># words with 4+ letters</span>
</span></span><span class="line"><span class="cl">  <span class="n">pattern</span> <span class="o">=</span> <span class="n">terms</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34;|&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">cmd</span> <span class="o">=</span> <span class="o">[</span><span class="s2">&#34;rg&#34;</span><span class="p">,</span> <span class="s2">&#34;-l&#34;</span><span class="p">,</span> <span class="s2">&#34;-i&#34;</span><span class="p">,</span> <span class="s2">&#34;-e&#34;</span><span class="p">,</span> <span class="n">pattern</span><span class="p">,</span> <span class="n">dir</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">files</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="no">Open3</span><span class="o">.</span><span class="n">capture2</span><span class="p">(</span><span class="o">*</span><span class="n">cmd</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">files</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">reject</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:empty?</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 2. Load entire files (up to a reasonable cap).</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">load_context</span><span class="p">(</span><span class="n">files</span><span class="p">,</span> <span class="ss">max_chars</span><span class="p">:</span> <span class="mi">600_000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">total</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">  <span class="n">files</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">path</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">body</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">next</span> <span class="k">if</span> <span class="n">total</span> <span class="o">+</span> <span class="n">body</span><span class="o">.</span><span class="n">size</span> <span class="o">&gt;</span> <span class="n">max_chars</span>
</span></span><span class="line"><span class="cl">    <span class="n">total</span> <span class="o">+=</span> <span class="n">body</span><span class="o">.</span><span class="n">size</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;## </span><span class="si">#{</span><span class="n">path</span><span class="si">}</span><span class="se">\n\n</span><span class="si">#{</span><span class="n">body</span><span class="si">}</span><span class="se">\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span><span class="o">.</span><span class="n">compact</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">---</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 3. Send to Claude with the question and the documents.</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">ask</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">context</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">chat</span> <span class="o">=</span> <span class="no">RubyLLM</span><span class="o">.</span><span class="n">chat</span><span class="p">(</span><span class="ss">model</span><span class="p">:</span> <span class="s2">&#34;anthropic/claude-sonnet-4-6&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">prompt</span> <span class="o">=</span> <span class="o">&lt;&lt;~</span><span class="no">PROMPT</span>
</span></span><span class="line"><span class="cl">    <span class="no">Você</span> <span class="n">tem</span> <span class="n">acesso</span> <span class="n">aos</span> <span class="n">documentos</span> <span class="n">abaixo</span><span class="o">.</span> <span class="no">Responda</span> <span class="n">a</span> <span class="n">pergunta</span> <span class="k">do</span> <span class="n">usuário</span>
</span></span><span class="line"><span class="cl">    <span class="n">usando</span> <span class="n">apenas</span> <span class="n">o</span> <span class="n">que</span> <span class="n">está</span> <span class="n">nos</span> <span class="n">documentos</span><span class="o">.</span> <span class="no">Cite</span> <span class="n">o</span> <span class="n">nome</span> <span class="k">do</span> <span class="n">arquivo</span> <span class="n">nas</span>
</span></span><span class="line"><span class="cl">    <span class="n">referências</span><span class="o">.</span> <span class="no">Se</span> <span class="n">a</span> <span class="n">resposta</span> <span class="n">não</span> <span class="n">estiver</span> <span class="n">nos</span> <span class="n">documentos</span><span class="p">,</span> <span class="n">diga</span> <span class="n">isso</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="o">---</span> <span class="no">DOCUMENTOS</span> <span class="o">---</span>
</span></span><span class="line"><span class="cl">    <span class="c1">#{context}</span>
</span></span><span class="line"><span class="cl">    <span class="o">---</span> <span class="no">FIM</span> <span class="no">DOS</span> <span class="no">DOCUMENTOS</span> <span class="o">---</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="ss">Pergunta</span><span class="p">:</span> <span class="c1">#{query}</span>
</span></span><span class="line"><span class="cl">  <span class="no">PROMPT</span>
</span></span><span class="line"><span class="cl">  <span class="n">chat</span><span class="o">.</span><span class="n">ask</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span><span class="o">.</span><span class="n">content</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">files</span> <span class="o">=</span> <span class="n">lexical_search</span><span class="p">(</span><span class="no">DOCS_DIR</span><span class="p">,</span> <span class="no">QUERY</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">abort</span> <span class="s2">&#34;nenhum arquivo bateu&#34;</span> <span class="k">if</span> <span class="n">files</span><span class="o">.</span><span class="n">empty?</span>
</span></span><span class="line"><span class="cl"><span class="nb">puts</span> <span class="s2">&#34;Encontrei </span><span class="si">#{</span><span class="n">files</span><span class="o">.</span><span class="n">size</span><span class="si">}</span><span class="s2"> arquivos. Carregando contexto...&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">context</span> <span class="o">=</span> <span class="n">load_context</span><span class="p">(</span><span class="n">files</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">puts</span> <span class="n">ask</span><span class="p">(</span><span class="no">QUERY</span><span class="p">,</span> <span class="n">context</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>About 40 lines. No Pinecone dependency, no vector schema, no re-indexing pipeline. You run it as <code>./ask.rb ./docs &quot;how do I configure the payment webhook&quot;</code> and that&rsquo;s it.</p>
<p>That example is one-shot. You run it, it answers, done. For a real chat, with multiple questions in a row over the same documents, the design changes. Instead of running <code>lexical_search</code> upfront and shoving everything into the context at once, you expose the search as a tool to the model. Then it&rsquo;s the agent that decides when it needs to pull more docs, what term to look for, which file is worth opening in full. That&rsquo;s how Claude Code actually works: <code>Glob</code>, <code>Grep</code> and <code>Read</code> are tools, and the model picks the sequence. <code>ruby_llm</code> supports tool calling, so you can do the same thing in Ruby. It looks something like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;ruby_llm&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;open3&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">DOCS_DIR</span> <span class="o">=</span> <span class="s2">&#34;./docs&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">SearchFiles</span> <span class="o">&lt;</span> <span class="no">RubyLLM</span><span class="o">::</span><span class="no">Tool</span>
</span></span><span class="line"><span class="cl">  <span class="n">description</span> <span class="s2">&#34;Procura arquivos cujo conteúdo casa com o padrão dado (regex). Retorna lista de paths.&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">param</span> <span class="ss">:pattern</span><span class="p">,</span> <span class="ss">desc</span><span class="p">:</span> <span class="s2">&#34;Padrão regex pra busca lexical (case-insensitive)&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="ss">pattern</span><span class="p">:)</span>
</span></span><span class="line"><span class="cl">    <span class="n">out</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="no">Open3</span><span class="o">.</span><span class="n">capture2</span><span class="p">(</span><span class="s2">&#34;rg&#34;</span><span class="p">,</span> <span class="s2">&#34;-l&#34;</span><span class="p">,</span> <span class="s2">&#34;-i&#34;</span><span class="p">,</span> <span class="s2">&#34;-e&#34;</span><span class="p">,</span> <span class="n">pattern</span><span class="p">,</span> <span class="no">DOCS_DIR</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">out</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">reject</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:empty?</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ReadFile</span> <span class="o">&lt;</span> <span class="no">RubyLLM</span><span class="o">::</span><span class="no">Tool</span>
</span></span><span class="line"><span class="cl">  <span class="n">description</span> <span class="s2">&#34;Lê o conteúdo completo de um arquivo do projeto.&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">param</span> <span class="ss">:path</span><span class="p">,</span> <span class="ss">desc</span><span class="p">:</span> <span class="s2">&#34;Caminho relativo do arquivo&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="ss">path</span><span class="p">:)</span>
</span></span><span class="line"><span class="cl">    <span class="no">File</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">rescue</span> <span class="o">=&gt;</span> <span class="n">e</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;erro: </span><span class="si">#{</span><span class="n">e</span><span class="o">.</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">chat</span> <span class="o">=</span> <span class="no">RubyLLM</span><span class="o">.</span><span class="n">chat</span><span class="p">(</span><span class="ss">model</span><span class="p">:</span> <span class="s2">&#34;anthropic/claude-sonnet-4-6&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="o">.</span><span class="n">with_tools</span><span class="p">(</span><span class="no">SearchFiles</span><span class="p">,</span> <span class="no">ReadFile</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="o">.</span><span class="n">with_instructions</span><span class="p">(</span><span class="o">&lt;&lt;~</span><span class="no">SYS</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">              <span class="no">Você</span> <span class="n">responde</span> <span class="n">perguntas</span> <span class="n">sobre</span> <span class="n">os</span> <span class="n">documentos</span> <span class="n">em</span> <span class="c1">#{DOCS_DIR}.</span>
</span></span><span class="line"><span class="cl">              <span class="no">Use</span> <span class="n">search_files</span> <span class="n">pra</span> <span class="n">encontrar</span> <span class="n">arquivos</span> <span class="n">relevantes</span> <span class="n">e</span> <span class="n">read_file</span>
</span></span><span class="line"><span class="cl">              <span class="n">pra</span> <span class="n">ler</span> <span class="n">o</span> <span class="n">conteúdo</span><span class="o">.</span> <span class="no">Sempre</span> <span class="n">cite</span> <span class="n">o</span> <span class="n">arquivo</span> <span class="n">na</span> <span class="n">resposta</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">            <span class="no">SYS</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kp">loop</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="nb">print</span> <span class="s2">&#34;&gt; &#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">msg</span> <span class="o">=</span> <span class="nb">gets</span><span class="o">&amp;.</span><span class="n">chomp</span>
</span></span><span class="line"><span class="cl">  <span class="k">break</span> <span class="k">if</span> <span class="n">msg</span><span class="o">.</span><span class="n">nil?</span> <span class="o">||</span> <span class="n">msg</span><span class="o">.</span><span class="n">empty?</span>
</span></span><span class="line"><span class="cl">  <span class="nb">puts</span> <span class="n">chat</span><span class="o">.</span><span class="n">ask</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span><span class="o">.</span><span class="n">content</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The model gets the question, decides whether it needs to search, calls <code>search_files</code>, sees what came back, decides whether it needs to open any file, calls <code>read_file</code>, and only then answers. On the next question it already has the previous context in the session and can ask for more if it needs to. The context only receives what the model asked for, not the whole grep dump from the earlier example.</p>
<p>The same idea works for databases: swap <code>rg</code> for a SQL query with <code>LIKE</code> or <code>tsvector</code> (Postgres full-text), load the relevant rows, throw them in the context. If you have 10k records in an internal database, this handles it. If you have 10 million, you start needing smarter pagination or a more serious pre-filtering layer. But the mental model is the same: <strong>dumb filter + smart reader</strong>.</p>
<h2>The point that matters<span class="hx:absolute hx:-mt-20" id="the-point-that-matters"></span>
    <a href="#the-point-that-matters" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The most interesting thing in all of this isn&rsquo;t even the Pinecone savings. It&rsquo;s that the nature of the bottleneck has changed. In 2023, the bottleneck was retrieval: the reader was small, slow, expensive, and you needed a clever retriever to fill the window with the bare minimum. In 2026, the bottleneck is reasoning over messy context: the reader is big, relatively fast, and cheap. So it makes more sense to have a dumb retriever with high recall and let the model do the heavy lifting.</p>
<p>Anyone still designing systems with the 2023 mindset is paying a premium to solve a problem whose shape has changed. RAG didn&rsquo;t die, the &ldquo;R&rdquo; got dumber and cheaper, and that&rsquo;s an upgrade. The vector DB vendors aren&rsquo;t going to tell you this, but it&rsquo;s the path the more experienced folks have been quietly walking.</p>
<p>The next wave of LLM applications, in my bet, is going to be dominated by the people who got this inversion. Smaller stacks, simpler infrastructure, generous context, and a whole lot less LangChain.</p>
<h2>What the recent literature says<span class="hx:absolute hx:-mt-20" id="what-the-recent-literature-says"></span>
    <a href="#what-the-recent-literature-says" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Before I close out, I went and checked what the research crowd published on this. Blog hot takes age in three months in this field, so it&rsquo;s better to look at the papers.</p>
<p><a href="https://arxiv.org/abs/2407.16833"target="_blank" rel="noopener"><strong>Retrieval Augmented Generation or Long-Context LLMs?</strong></a>, out of Google DeepMind, published at EMNLP 2024, is probably the most cited piece in the debate. Their conclusion: when the model has enough resources, long context beats RAG on average quality, but RAG is still much cheaper in tokens. They propose Self-Route, an approach where the model itself decides whether it needs retrieval or whether it can just go straight through context. The token savings are big and the quality loss is small.</p>
<p>Then <a href="https://openreview.net/forum?id=CLF25dahgA"target="_blank" rel="noopener"><strong>LaRA</strong></a>, presented at ICML 2025, is more measured. The authors built 2326 test cases across four QA task types and three long-context types, ran them across 11 different LLMs, and the conclusion was: there is no silver bullet. The choice between RAG and long context depends on the model, the context size, the task type, and the retrieval characteristics. RAG wins on dialogue and generic queries, long context wins on Wikipedia-style QA.</p>
<p><a href="https://arxiv.org/abs/2501.01880"target="_blank" rel="noopener"><strong>Long Context vs. RAG for LLMs: An Evaluation and Revisits</strong></a>, from January 2025, is the one that most reinforces this post&rsquo;s thesis. Long context tends to beat RAG on QA benchmarks, especially when the base document is stable. Summarization-based retrieval comes close, and chunk-based retrieval lags behind. In other words: the old way, chunk plus embed plus top-k, is the one that comes out worst.</p>
<p>Worth keeping on the radar too is the original <a href="https://arxiv.org/abs/2307.03172"target="_blank" rel="noopener"><strong>Lost in the Middle</strong></a> (Liu et al., 2023, published in TACL in 2024). That&rsquo;s the paper that showed even models with big windows have performance that depends on the position of the relevant information. Stuff at the beginning or end of the context is found easily; stuff in the middle degrades. For a long time this got used as the argument against long context, but the paper is from 2023, with 2023 models. Today&rsquo;s models, the Claude 4.x and Gemini 3.x line, handle the middle a lot better. It&rsquo;s not a solved problem, but it&rsquo;s much smaller than it was.</p>
<p>On the lexical retrieval side, <a href="https://arxiv.org/abs/2104.08663"target="_blank" rel="noopener"><strong>BEIR</strong></a> is still the canonical reference. The classic result is that BM25, all the way from the 90s, is still ridiculously competitive in out-of-domain scenarios. Dense models only win consistently when you have in-domain data to fine-tune the embeddings. In zero-shot scenarios, which is where most projects live, BM25 is hard to beat without serious work.</p>
<p>To wrap up, the <a href="https://www.anthropic.com/news/contextual-retrieval"target="_blank" rel="noopener"><strong>Anthropic post on Contextual Retrieval</strong></a>, from September 2024, is the most practical piece on the list. They show that combining contextual embedding with contextual BM25 drops the top-20 failure rate from 5.7% to 2.9%. Add a reranker and it drops to 1.9%. Important detail: BM25 is the centerpiece of their result, not a sidekick. The right reading is &ldquo;lexical plus vector plus reranker is the combination that works.&rdquo; Anyone who can only pick one picks BM25 and still gets pretty far.</p>
<p>To sum up what we can actually nail down: the literature isn&rsquo;t claiming &ldquo;RAG is dead.&rdquo; It&rsquo;s saying that long context, when you can use it, tends to win on quality. It&rsquo;s saying RAG&rsquo;s cost is still its main argument. It&rsquo;s saying lexical BM25 is much stronger than the vector DB marketing makes it sound. And it&rsquo;s saying that when you really do need heavy retrieval, the robust combination is hybrid (lexical plus vector plus reranker), not pure vector. All of that lines up with what I&rsquo;ve been defending in practice.</p>
<h2>Sources<span class="hx:absolute hx:-mt-20" id="sources"></span>
    <a href="#sources" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li>Li, Z. et al. (2024). <a href="https://arxiv.org/abs/2407.16833"target="_blank" rel="noopener">Retrieval Augmented Generation or Long-Context LLMs? A Comprehensive Study and Hybrid Approach</a>. EMNLP 2024 Industry Track.</li>
<li>Yuan, K. et al. (2025). <a href="https://openreview.net/forum?id=CLF25dahgA"target="_blank" rel="noopener">LaRA: Benchmarking Retrieval-Augmented Generation and Long-Context LLMs – No Silver Bullet for LC or RAG Routing</a>. ICML 2025.</li>
<li>Yu, T. et al. (2025). <a href="https://arxiv.org/abs/2501.01880"target="_blank" rel="noopener">Long Context vs. RAG for LLMs: An Evaluation and Revisits</a>. arXiv:2501.01880.</li>
<li>Liu, N. F. et al. (2023). <a href="https://arxiv.org/abs/2307.03172"target="_blank" rel="noopener">Lost in the Middle: How Language Models Use Long Contexts</a>. TACL 2024.</li>
<li>Thakur, N. et al. (2021). <a href="https://arxiv.org/abs/2104.08663"target="_blank" rel="noopener">BEIR: A Heterogenous Benchmark for Zero-shot Evaluation of Information Retrieval Models</a>. NeurIPS Datasets and Benchmarks 2021.</li>
<li>Anthropic (2024). <a href="https://www.anthropic.com/news/contextual-retrieval"target="_blank" rel="noopener">Introducing Contextual Retrieval</a>. Blog post.</li>
<li>Akita, F. (2026). <a href="/en/2026/03/31/claude-code-source-code-leaked-what-we-found-inside/">Claude Code&rsquo;s Source Code Leaked. Here&rsquo;s What We Found Inside.</a> — my coverage of the leak, with more detail on the memory architecture, KAIROS and <code>autoDream</code>.</li>
</ul>
]]></content:encoded><category>llms</category><category>software-architecture</category><category>databases</category></item><item><title>The Economics of Software Development</title><link>https://akitaonrails.github.io/en/2017/06/22/the-economics-of-software-development/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2017/06/22/the-economics-of-software-development/</guid><pubDate>Thu, 22 Jun 2017 10:56:00 -0300</pubDate><description>&lt;p&gt;The software development community is boiling nowadays with so many options all at once. You have dozens of active and very good languages such as Go, &lt;a href="https://akitaonrails.github.io/elixir"&gt;Elixir&lt;/a&gt;, Clojure. Dozens of very good frameworks, both in the back-end and front-end including React, Elm. Dozens of sound methodologies from good old Agile stuff all the way to Continuous Deployment with microservices.&lt;/p&gt;
&lt;p&gt;Now, you&amp;rsquo;re a small tech startup or even a small team in a big corporation. How to even start?&lt;/p&gt;</description><content:encoded><![CDATA[<p>The software development community is boiling nowadays with so many options all at once. You have dozens of active and very good languages such as Go, <a href="/elixir">Elixir</a>, Clojure. Dozens of very good frameworks, both in the back-end and front-end including React, Elm. Dozens of sound methodologies from good old Agile stuff all the way to Continuous Deployment with microservices.</p>
<p>Now, you&rsquo;re a small tech startup or even a small team in a big corporation. How to even start?</p>
<p>And the recommendation is: do the smallest thing that works first. Always. Of course, avoid &ldquo;quick and dirty&rdquo; as much as you can. But don&rsquo;t get paranoid and over-engineer too much.</p>
<p>Over-engineering is as expensive as doing things &ldquo;dirty&rdquo;.</p>
<h3>Add &ldquo;Time&rdquo; to your equation<span class="hx:absolute hx:-mt-20" id="add-time-to-your-equation"></span>
    <a href="#add-time-to-your-equation" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>What most people do very wrong is disregarding the variable of &ldquo;Time&rdquo;.</p>
<p>Everybody goes to tech conferences or read fancy blog posts, or watch flashy screencasts. Their wrong conclusion is: <em>&ldquo;Netflix uses it, therefore it should be good for me, because I want to become Netflix&rdquo;</em>.</p>
<p>The wrong assumption is that Netflix, or Google, or Facebook, or Spotify, is a static system that always functioned the way they advertise it today.</p>
<p>People forget that every unicorn company had a <strong>day-1</strong>. And on day-1 they did not use microservices. They did not have React.js. They did not have Go or Elixir. Some of them didn&rsquo;t even have good programmers to begin with. Netflix started as a VHS renting service, remember that? Google started off in a dorm-room with off-the-shelf hardware components assembled in a <a href="http://www.complex.com/pop-culture/2013/02/50-things-you-didnt-know-about-google/lego-server-rack"target="_blank" rel="noopener">Lego-based rack</a>.</p>
<p>You are a fan of some celebrity. That celebrity has a Lamborghini car. So you want to be like that celebrity one day. What do you do? Do you buy the same Lamborghini he has?</p>
<p>If you are that stupid, the only thing you will end up with is a HUGE debt to pay.</p>
<p>Stop envying the rich guy&rsquo;s Lamborghini.</p>
<h3>(Technical) Debt<span class="hx:absolute hx:-mt-20" id="technical-debt"></span>
    <a href="#technical-debt" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>This is neither a new term nor a new theme. Every decision you make in programming is a <strong>compromise</strong> between what the &ldquo;future-ideal&rdquo; should be and what you can actually do right now. Loan to buy the equivalent Lamborghini too soon, and you will have to deal with an unpayable (Technical) Debt. You will stall, you will stop, and you will do nothing but pay that debt from now on.</p>
<p>You want to &ldquo;code faster&rdquo; therefore you skip writing automated tests, because you assume it will slow you down. And you&rsquo;re right. Test-driven development doesn&rsquo;t make you fast at first, and it isn&rsquo;t supposed to. It protects your future self from your present self, and keeps you from piling up Technical Debt.</p>
<p>Again, you forget the &ldquo;Time&rdquo; variable.</p>
<p>You write and deliver fast for the 1st month. After the first version is deployed, now you will be obliged to start paying the debt. Your productivity will slow down. Regression bugs will show up. Every new feature you try to add breaks something in unexpected ways. Because you don&rsquo;t have automated tests, you will keep fixing the same things many times over. Debt will catch up, and you will pay. One way or the other.</p>
<p>Minimal test suites are like <strong>Insurance</strong>. You don&rsquo;t need it right now, on day-1. But on day-100 you will be so glad you have it.</p>
<p>Doing too much microservices on day-1 is debt. It feels great that you&rsquo;re doing that fancy thing you read in a blog post. On day-100, with your 3 people team and a dozen microservices, from now on you will do nothing but pay the accumulated debt, with <strong>Interest</strong>. Every new microservices deployment breaks your system. And of course you didn&rsquo;t add monitoring, you didn&rsquo;t add integrated tests. So every time you code something, something else unexpectedly breaks.</p>
<p>Enjoying that early Lamborghini now?</p>
<h3>The Mythical Man-Month<span class="hx:absolute hx:-mt-20" id="the-mythical-man-month"></span>
    <a href="#the-mythical-man-month" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Philip Calçado writes very good posts and presentations about microservices in the right way. If you&rsquo;re serious about that subject I strongly recommend that you read his posts, such as his <a href="http://philcalcado.com/2017/06/11/calcados_microservices_prerequisites.html"target="_blank" rel="noopener">&ldquo;Prerequisites&rdquo;</a> post or his <a href="https://www.infoq.com/news/2017/05/economics-microservices"target="_blank" rel="noopener">&ldquo;Economics&rdquo;</a> presentation.</p>
<p>He correctly remembers Fred Brooks&rsquo; <strong>The Mythical Man-Month</strong>. I urge developers to read this small book. It&rsquo;s uncanny how the entire industry is still repeating the very same mistakes Brooks reports in his book from projects in the 60&rsquo;s!</p>
<p>To me, microservices is a by-product of tech companies with too many developers. Once you break through the 5-developers team a &ldquo;monolithic&rdquo; system with poor organization and few automated tests can become cumbersome to deal with. So the consequence is the desire to break it down. You make 2 teams and 2 microservices and coordinate. And the teams start to isolate, and play the finger-pointing game when new bugs are reported (<em>&ldquo;it&rsquo;s the other team&rsquo;s microservice&rsquo;s fault&rdquo;</em>).</p>
<p>That&rsquo;s <a href="http://www.melconway.com/Home/Conways_Law.html"target="_blank" rel="noopener">Conway&rsquo;s Law</a> if you do it wrong:</p>
<blockquote>
  <p>Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization&rsquo;s communication structure.</p>

</blockquote>
<p>Also in play is another chapter in Brooks&rsquo; book: The <a href="http://wiki.c2.com/?SecondSystemEffect"target="_blank" rel="noopener">Second System Effect</a>. Every developer, tech startup, has to assume that the first version of anything is not the best design. And the second system (the first big rewrite) is <strong>the most dangerous</strong> system you will write. As Brooks states, it goes like this:</p>
<blockquote>
  <p>An architect&rsquo;s first work is apt to be spare and clean. He knows he doesn&rsquo;t know what he&rsquo;s doing, so he does it carefully and with great restraint.</p>

</blockquote>
<blockquote>
  <p>As he designs the first work, frill after frill and embellishment after embellishment occur to him. These get stored away to be used &ldquo;next time.&rdquo; Sooner or later the first system is finished, and the architect, with firm confidence and a demonstrated mastery of that class of systems, is ready to build a second system.</p>

</blockquote>
<blockquote>
  <p>This second is the most dangerous system a man ever designs. When he does his third and later ones, his prior experiences will confirm each other as to the general characteristics of such systems, and their differences will identify those parts of his experience that are particular and not generalizable.</p>

</blockquote>
<blockquote>
  <p>The general tendency is to over-design the second system, using all the ideas and frills that were cautiously sidetracked on the first one. The result, as Ovid says, is a &ldquo;big pile.&rdquo;</p>

</blockquote>
<p>Then, what happens? The CEO, the board, the investors, or whatever, start doing the wrong thing: hiring more and more people. Loan more and more. Instead of paying the debts they end up making more debt. They fell for what Brooks reported in the first chapter and what gives the name to the book: The Mythical Man-Month.</p>
<blockquote>
  <p>Adding manpower to a late software project makes it later.</p>

</blockquote>
<blockquote>
  <p>Complex programming projects cannot be perfectly partitioned into discrete tasks that can be worked on without communication between the workers and without establishing a set of complex interrelationships between tasks and the workers performing them.</p>

</blockquote>
<blockquote>
  <p>Therefore, assigning more programmers to a project running behind schedule will make it even later. This is because the time required for the new programmers to learn about the project and the increased communication overhead will consume an ever increasing quantity of the calendar time available.</p>

</blockquote>
<p>Again, it&rsquo;s foregoing the sense of the &ldquo;Time&rdquo; variable. And confusing &ldquo;debt&rdquo; for &ldquo;investment&rdquo;.</p>
<p>First of all, do yourself a favor and <a href="http://amzn.to/2sFbkWq"target="_blank" rel="noopener">read the goddamn book</a>, twice.</p>
<h3>Do Agile right! And no, it&rsquo;s not Kanban!<span class="hx:absolute hx:-mt-20" id="do-agile-right-and-no-its-not-kanban"></span>
    <a href="#do-agile-right-and-no-its-not-kanban" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>If there is one good thing the whole Lean noise generated is the notion of &ldquo;Minimum Viable Product&rdquo; or MVP. People call it &ldquo;prototype&rdquo;. Some call it &ldquo;launching Beta&rdquo; or simply &ldquo;version 1.0&rdquo;. Doesn&rsquo;t matter. It&rsquo;s the realization that you don&rsquo;t know much at the beginning so overengineering on the first version is a waste of time. Lean is about controlling &ldquo;waste&rdquo;, so we do the minimal that works, gauge results and work from there.</p>
<p>The cynical will say that any prototype that reaches production will never die. And they&rsquo;re not wrong.</p>
<p>The balance is to not do the &ldquo;quick and dirty&rdquo; version. That&rsquo;s what we have Agile techniques for. Do the minimal, organize the minimal. That&rsquo;s why we have object-oriented design patterns, from GoF to DDD. You don&rsquo;t need to do EVERY pattern, and that&rsquo;s exactly what &ldquo;overengineering&rdquo; means. But you can do the minimal monolith that will allow you to evolve later.</p>
<p>People criticize Ruby on Rails for not being organized &ldquo;enough&rdquo;. <a href="https://apotonick.wordpress.com/2015/09/05/the-only-alternative-to-a-rails-monolith-are-micro-services-bullshit/"target="_blank" rel="noopener">Nick Sutterer</a>, the creator of <a href="https://github.com/trailblazer/trailblazer"target="_blank" rel="noopener">Trailblazer</a>, has a good point.</p>
<p>Rails done wrong is bad. The conclusion should NOT be: <em>&ldquo;let&rsquo;s do microservices&rdquo;</em> though. That jump in faith is idiotic and makes no sense for all the reasons I mentioned above.</p>
<p><em>&ldquo;Let&rsquo;s do proper Agile stuff and proper object-oriented stuff&rdquo;</em> should be the initial answer.</p>
<p>And by &ldquo;Agile&rdquo; forget about idiotic post-its, idiotic numerology-based estimation equations (this is a whole post in itself, because story points and velocity are useful, but adding Montecarlo and other Gaussian-based stuff are not). The only &ldquo;Agile&rdquo; things you should be concerned about are the <a href="http://www.extremeprogramming.org/"target="_blank" rel="noopener">Extreme Programming (XP) techniques</a>, <strong>including</strong> Iteration-based timeboxes.</p>
<p>You <strong>MUST</strong> do timeboxes. Stop, re-assess, change directions, and then keep going. The model of &ldquo;Pull&rdquo; is only reasonable when your direction is very, very clear, written in stone and unchangeable, like in a factory production line! (Which is exactly where the concept of Pull, and Lean, was actually born!)</p>
<p>Iterations, like automated tests, are like Insurance. You can never avoid all waste, but you can minimize it. You can afford to throw away an Iteration-worth of work. After the iteration you measure the results, and throw it away if necessary, changing direction in the process. Throwing useless stuff away is as important as adding new stuff. If you just add, you have a <a href="https://en.wikipedia.org/wiki/Compulsive_hoarding"target="_blank" rel="noopener">hoarding disorder</a>!</p>
<p>If in doubt, do XP. Yes, it feels more &ldquo;difficult&rdquo;, and Kanban is &ldquo;simpler&rdquo; to explain. Now, is that a good reason?</p>
<h3>Forget about Raw Performance when you DON&rsquo;T need it!<span class="hx:absolute hx:-mt-20" id="forget-about-raw-performance-when-you-dont-need-it"></span>
    <a href="#forget-about-raw-performance-when-you-dont-need-it" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Another problem is choosing languages or framework because of performance.</p>
<p>If you&rsquo;re in the web development business, this is a huge WASTE.</p>
<p>Understand this truth: your servers will IDLE most of the time. And if you feel like using your web app is slow, the slowness comes from the POOR programming you did, not from the language. And no good language will rescue a bad programmer. I always say that if performance was that important, we should all be doing C.</p>
<p>Most of what you serve in HTTP-based apps, be it user-readable content, be it API GET results, can be <strong>CACHED</strong>! If you&rsquo;re not using a <a href="http://www.akitaonrails.com/2015/08/25/small-bites-adicionando-um-cdn-ao-seu-site-a-forma-facil"target="_blank" rel="noopener">CDN</a>, you&rsquo;re doing it wrong.</p>
<p>Yeah, yeah, yeah, you think you&rsquo;re building the next Spotify. You&rsquo;re not, at least not 99% of you. And the 1% doing custom engineering, with custom techniques and custom stack, successfully: you&rsquo;re the 1%. Actually I&rsquo;d say you&rsquo;re a fraction of the 1%. Do not assume that what you do is good for the rest of the population.</p>
<p>90% of what most small companies and solo web developers need is just a Shopify account and a vanilla Wordpress installation. And that&rsquo;s it.</p>
<blockquote>
  <p>Are you doing infrastructure development like building custom components to Docker, Kubernetes, Terraform? Command-line tools? Daemons? Then choose GO.</p>

</blockquote>
<blockquote>
  <p>Are you doing embedded libraries? Maybe the next generation OpenSSL? Drivers? Then choose RUST.</p>

</blockquote>
<blockquote>
  <p>Are you doing mobile development? Then don&rsquo;t have a lot of choice: do Swift for iOS, do Kotlin/Java for Android. Or do React Native for simpler apps.</p>

</blockquote>
<blockquote>
  <p>Are you really doing the next Whatsapp? The next Waze? The next Snap? You have hundreds or thousands of users in need of long-lived connections over unreliable network doing broadcasts of messages? Or you&rsquo;re building the next evolution of distributed NoSQL databases? Or anything that actually has the proper meaning of &ldquo;Distributed&rdquo; in its definition? Then choose ELIXIR.</p>

</blockquote>
<blockquote>
  <p>Are you doing a CRUD based web application? Go RAILS and never look back.</p>

</blockquote>
<blockquote>
  <p>Are you doing all of the above? Use all the alternatives then. And I hope you have the budget, because you need a big team.</p>

</blockquote>
<h3>Conclusion<span class="hx:absolute hx:-mt-20" id="conclusion"></span>
    <a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The Economics only makes sense when you consider the day-1. Do not forget the &ldquo;Time&rdquo; variable. It makes all the difference.</p>
<p>You will always get Debt to pay. So be smart in which kind of debt you choose. Because you will have to pay it back eventually.</p>
<p>Again, read Brooks. He said it decades ago: <a href="http://worrydream.com/refs/Brooks-NoSilverBullet.pdf"target="_blank" rel="noopener">THERE IS NO SILVER BULLET</a>. Don&rsquo;t bullshit yourself. A new language, a new framework, a new architecture. None of those will save you.</p>
<p>In any tech endeavor, code is not the only thing to worry about. I would go as far as to say that in a tech startup, code is only 20% of the problem. A tech startup is just a company, like any other. If you&rsquo;re the founder or CEO, you have to deal with all the remaining 80%: marketing, accounting, human resources, legal, etc. It&rsquo;s already difficult enough without your tech team getting you unwanted Technical Debt, the unneeded Lamborghini, that you won&rsquo;t be able to pay.</p>
<p>You can start humble on day-1. Keep evolving, continuously. This is the core of any Agile or Lean process: <a href="https://www.graphicproducts.com/articles/what-is-kaizen/"target="_blank" rel="noopener">KAIZEN</a>. Choose smart debts, pay them a small bit at a time, continuously. It&rsquo;s the same reasoning as getting a loan in the bank.</p>
<blockquote>
  <p>Netflix day-7,200 is NOT your day-1.</p>

</blockquote>
<blockquote>
  <p>Facebook day-4,800 is NOT your day-1.</p>

</blockquote>
<blockquote>
  <p>Google day-6,809 is NOT your day-1.</p>

</blockquote>
<blockquote>
  <p>Be humble. Deliver fast. Pay your Debts. Keep evolving continuously. Stop believing in fairy tales and silver bullets.</p>

</blockquote>
]]></content:encoded><category>software-engineering</category><category>agile</category><category>software-architecture</category><category>off-topic</category></item><item><title>Sharing models between Rails Apps - Part 2</title><link>https://akitaonrails.github.io/en/2016/10/03/sharing-models-between-rails-apps-part-2/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2016/10/03/sharing-models-between-rails-apps-part-2/</guid><pubDate>Mon, 03 Oct 2016 15:57:00 -0300</pubDate><description>&lt;p&gt;Let&amp;rsquo;s continue from where I left off in &lt;a href="http://www.akitaonrails.com/en/2016/10/03/compartilhando-models-entre-aplicacoes-rails-parte-1"target="_blank" rel="noopener"&gt;Part 1&lt;/a&gt; where I quickly described how you can extract reusable model logic from a Rails app into a testable Rubygem.&lt;/p&gt;
&lt;p&gt;If I were building a secondary Rails app connecting directly to the same database as the first, I could just add the dependency to the extracted gem:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;central-support&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;github&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Codeminer42/cm42-central-support&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;master&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;central/support&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copy code"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Then recreate the models including the same Concerns, and make sure I remove the ability to &lt;code&gt;bin/rails db:migrate&lt;/code&gt; from the secondary app (by creating empty db tasks with the same name, for example).&lt;/p&gt;</description><content:encoded><![CDATA[<p>Let&rsquo;s continue from where I left off in <a href="http://www.akitaonrails.com/en/2016/10/03/compartilhando-models-entre-aplicacoes-rails-parte-1"target="_blank" rel="noopener">Part 1</a> where I quickly described how you can extract reusable model logic from a Rails app into a testable Rubygem.</p>
<p>If I were building a secondary Rails app connecting directly to the same database as the first, I could just add the dependency to the extracted gem:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;central-support&#39;</span><span class="p">,</span> <span class="ss">github</span><span class="p">:</span> <span class="s1">&#39;Codeminer42/cm42-central-support&#39;</span><span class="p">,</span> <span class="ss">branch</span><span class="p">:</span> <span class="s1">&#39;master&#39;</span><span class="p">,</span> <span class="nb">require</span><span class="p">:</span> <span class="s1">&#39;central/support&#39;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Then recreate the models including the same Concerns, and make sure I remove the ability to <code>bin/rails db:migrate</code> from the secondary app (by creating empty db tasks with the same name, for example).</p>
<p>By the way, this is one big caveat that I didn&rsquo;t address in Part 1: up to this point, the schema was <strong>frozen</strong> in the <a href="https://github.com/Codeminer42/cm42-central-support/blob/master/spec/support/rails_app/db/schema.rb"target="_blank" rel="noopener">central-support gem</a>.</p>
<p>From now on, you must control the evolution of the tables mapped in the gem from within the gem. The best approach is to use the <code>spec/support/rails_app</code> and normally create new migrations with <code>bin/rails g migration</code> from there. Then you must move the migration to the <code>lib/generators/central/templates/migrations</code> folder.</p>
<p>The <a href="https://github.com/Codeminer42/cm42-central-support/blob/master/lib/generators/central/install_generator.rb"target="_blank" rel="noopener"><code>lib/generators/central/install_generator.rb</code></a> will take care of making a <code>central:install</code> task available that will dutifully put new migrations into your application&rsquo;s <code>db/migrate</code> folder as usual. You just have to <code>bundle update central-support</code> to get the newest changes, run <code>bin/rails g central:install</code> to create the new migrations (it will automatically skip existing ones) and run the normal <code>bin/rails db:migrate</code>. A migration generator code is very simple, you can do it like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;securerandom&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;rails/generators&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;rails/generators/base&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;rails/generators/active_record&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">Central</span>
</span></span><span class="line"><span class="cl">  <span class="k">module</span> <span class="nn">Generators</span>
</span></span><span class="line"><span class="cl">    <span class="k">class</span> <span class="nc">InstallGenerator</span> <span class="o">&lt;</span> <span class="no">Rails</span><span class="o">::</span><span class="no">Generators</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">      <span class="kp">include</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Generators</span><span class="o">::</span><span class="no">Migration</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">source_root</span> <span class="no">File</span><span class="o">.</span><span class="n">expand_path</span><span class="p">(</span><span class="s2">&#34;../templates&#34;</span><span class="p">,</span> <span class="bp">__FILE__</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">def</span> <span class="nf">create_migrations</span>
</span></span><span class="line"><span class="cl">        <span class="no">Dir</span><span class="o">[</span><span class="s2">&#34;</span><span class="si">#{</span><span class="nb">self</span><span class="o">.</span><span class="n">class</span><span class="o">.</span><span class="n">source_root</span><span class="si">}</span><span class="s2">/migrations/*.rb&#34;</span><span class="o">].</span><span class="n">sort</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">filepath</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">          <span class="nb">name</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">basename</span><span class="p">(</span><span class="n">filepath</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="n">migration_template</span> <span class="s2">&#34;migrations/</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">,</span> <span class="s2">&#34;db/migrate/</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">,</span> <span class="ss">skip</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">          <span class="nb">sleep</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="k">end</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The <code>migration_template</code> will take care of adding the proper timestamp to the migration file, so you don&rsquo;t have to add it manually and the template file name can be something plain such as <a href="https://github.com/Codeminer42/cm42-central-support/blob/master/lib/generators/central/templates/migrations/add_role_field_to_users.rb"target="_blank" rel="noopener"><code>migrations/add_role_field_to_user.rb</code></a>.</p>
<p>All that having been said, there is a second challenge I added to my internal secondary app: I wanted it to have its own main database and use Central&rsquo;s database as a secondary read-only source.</p>
<p>So migrations in the secondary app (let&rsquo;s just call it Central-2) will run against its own main database, not against the main Central&rsquo;s database. This adds the following problem: the test suite must be able to create and migrate both test databases, in isolation from Central. Only in production should Central-2 connect to Central&rsquo;s database.</p>
<p>Every Rails application has a <code>config/database.yml.sample</code> and a <code>db/schema.rb</code>, so I started by creating a <code>config/database_central.yml.sample</code> and a <code>db_central/schema.rb</code>.</p>
<p>The <code>config/database_central.yml.sample</code> is already interesting:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">development</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">adapter</span><span class="p">:</span><span class="w"> </span><span class="l">postgresql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">encoding</span><span class="p">:</span><span class="w"> </span><span class="l">unicode</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">timeout</span><span class="p">:</span><span class="w"> </span><span class="m">5000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">database</span><span class="p">:</span><span class="w"> </span><span class="l">fulcrum_development</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pool</span><span class="p">:</span><span class="w"> </span><span class="m">5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># Warning: The database defined as &#34;test&#34; will be erased and</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># re-generated from your development database when you run &#34;rake&#34;.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># Do not set this db to the same as development or production.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">test</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">adapter</span><span class="p">:</span><span class="w"> </span><span class="l">postgresql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">encoding</span><span class="p">:</span><span class="w"> </span><span class="l">unicode</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">timeout</span><span class="p">:</span><span class="w"> </span><span class="m">5000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="l">&lt;% if ENV[&#39;DATABASE_CENTRAL_URL&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;DATABASE_CENTRAL_URL&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="l">&lt;% else %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">database</span><span class="p">:</span><span class="w"> </span><span class="l">central_test</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="l">&lt;% end %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pool</span><span class="p">:</span><span class="w"> </span><span class="m">5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">production</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">adapter</span><span class="p">:</span><span class="w"> </span><span class="l">postgresql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">encoding</span><span class="p">:</span><span class="w"> </span><span class="l">unicode</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">timeout</span><span class="p">:</span><span class="w"> </span><span class="m">5000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;DATABASE_CENTRAL_URL&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pool</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;DB_POOL&#39;] || 5 %&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In production, it will use the <code>DATABASE_CENTRAL_URL</code> environment variable to connect to Central&rsquo;s main database.</p>
<p>When running tests locally, it will simply connect to a local database named <code>central_test</code>.</p>
<p>Now, while running tests at Gitlab-CI (or any other CI for that matter), I have to configure the <code>DATABASE_CENTRAL_URL</code> to point to a secondary Postgresql test database.</p>
<p>For Gitlab, this is how I configure the build script:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">codeminer42/ci-ruby:2.3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">postgres:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">cache</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">central-bot</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">untracked</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">paths</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">.ci_cache/</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">variables</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">RAILS_ENV</span><span class="p">:</span><span class="w"> </span><span class="l">test</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">DATABASE_URL</span><span class="p">:</span><span class="w"> </span><span class="l">postgresql://postgres:@postgres</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">DATABASE_CENTRAL_URL</span><span class="p">:</span><span class="w"> </span><span class="l">postgresql://postgres:@postgres/central_test</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">before_script</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">bundle install --without development production -j $(nproc) --path .ci_cache</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">cp config/database.yml.sample config/database.yml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">cp config/database_central.yml.sample config/database_central.yml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">bin/rails --trace central:db:create central:db:schema:load</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">bin/rails --trace db:create db:schema:load</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">test</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">script</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">bundle exec rspec</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Notice how I copy the &ldquo;.sample&rdquo; config files to make sure they exist. And then how I run tasks you know such as <code>db:create db:schema:load</code> to create the normal test database, but also tasks you don&rsquo;t know such as <code>central:db:create central:db:schema:load</code>.</p>
<p>I defined those tasks in <code>lib/tasks/db_central.rake</code> like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">task</span> <span class="ss">spec</span><span class="p">:</span> <span class="o">[</span><span class="s2">&#34;central:db:test:prepare&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">namespace</span> <span class="ss">:central</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">namespace</span> <span class="ss">:db</span> <span class="k">do</span> <span class="o">|</span><span class="n">ns</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">task</span> <span class="s2">&#34;environment:set&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="no">Rake</span><span class="o">::</span><span class="no">Task</span><span class="o">[</span><span class="s2">&#34;db:environment:set&#34;</span><span class="o">].</span><span class="n">invoke</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">task</span> <span class="ss">:drop</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="no">Rake</span><span class="o">::</span><span class="no">Task</span><span class="o">[</span><span class="s2">&#34;db:drop&#34;</span><span class="o">].</span><span class="n">invoke</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">task</span> <span class="ss">:create</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="no">Rake</span><span class="o">::</span><span class="no">Task</span><span class="o">[</span><span class="s2">&#34;db:create&#34;</span><span class="o">].</span><span class="n">invoke</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">task</span> <span class="ss">:setup</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="no">Rake</span><span class="o">::</span><span class="no">Task</span><span class="o">[</span><span class="s2">&#34;db:setup&#34;</span><span class="o">].</span><span class="n">invoke</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">task</span> <span class="ss">:reset</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="no">Rake</span><span class="o">::</span><span class="no">Task</span><span class="o">[</span><span class="s2">&#34;db:reset&#34;</span><span class="o">].</span><span class="n">invoke</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">task</span> <span class="ss">:migrate</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="no">Rake</span><span class="o">::</span><span class="no">Task</span><span class="o">[</span><span class="s2">&#34;db:migrate&#34;</span><span class="o">].</span><span class="n">invoke</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">task</span> <span class="ss">:rollback</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="no">Rake</span><span class="o">::</span><span class="no">Task</span><span class="o">[</span><span class="s2">&#34;db:rollback&#34;</span><span class="o">].</span><span class="n">invoke</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">task</span> <span class="ss">:seed</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="no">Rake</span><span class="o">::</span><span class="no">Task</span><span class="o">[</span><span class="s2">&#34;db:seed&#34;</span><span class="o">].</span><span class="n">invoke</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">task</span> <span class="ss">:version</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="no">Rake</span><span class="o">::</span><span class="no">Task</span><span class="o">[</span><span class="s2">&#34;db:version&#34;</span><span class="o">].</span><span class="n">invoke</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">namespace</span> <span class="ss">:schema</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">task</span> <span class="ss">:load</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">        <span class="no">Rake</span><span class="o">::</span><span class="no">Task</span><span class="o">[</span><span class="s2">&#34;db:schema:load&#34;</span><span class="o">].</span><span class="n">invoke</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">task</span> <span class="ss">:dump</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">        <span class="no">Rake</span><span class="o">::</span><span class="no">Task</span><span class="o">[</span><span class="s2">&#34;db:schema:dump&#34;</span><span class="o">].</span><span class="n">invoke</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">namespace</span> <span class="ss">:test</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">task</span> <span class="ss">:prepare</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">        <span class="no">Rake</span><span class="o">::</span><span class="no">Task</span><span class="o">[</span><span class="s2">&#34;db:test:prepare&#34;</span><span class="o">].</span><span class="n">invoke</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># append and prepend proper tasks to all the tasks defined here above</span>
</span></span><span class="line"><span class="cl">    <span class="n">ns</span><span class="o">.</span><span class="n">tasks</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">task</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">task</span><span class="o">.</span><span class="n">enhance</span> <span class="o">[</span><span class="s2">&#34;central:set_custom_config&#34;</span><span class="o">]</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">        <span class="no">Rake</span><span class="o">::</span><span class="no">Task</span><span class="o">[</span><span class="s2">&#34;central:revert_to_original_config&#34;</span><span class="o">].</span><span class="n">invoke</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">task</span> <span class="ss">:set_custom_config</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># save current vars</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@original_config</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="ss">env_schema</span><span class="p">:</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;SCHEMA&#39;</span><span class="o">]</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">config</span><span class="p">:</span> <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">dup</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># set config variables for custom database</span>
</span></span><span class="line"><span class="cl">    <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;SCHEMA&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;db_central/schema.rb&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">paths</span><span class="o">[</span><span class="s1">&#39;db&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="o">[</span><span class="s2">&#34;db_central&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">paths</span><span class="o">[</span><span class="s1">&#39;db/migrate&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="o">[</span><span class="s2">&#34;db_central/migrate&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">paths</span><span class="o">[</span><span class="s1">&#39;db/seeds&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="o">[</span><span class="s2">&#34;db_central/seeds.rb&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">paths</span><span class="o">[</span><span class="s1">&#39;config/database&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="o">[</span><span class="s2">&#34;config/database_central.yml&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Tasks</span><span class="o">::</span><span class="no">DatabaseTasks</span><span class="o">.</span><span class="n">database_configuration</span> <span class="o">=</span> <span class="no">CM</span><span class="p">(</span><span class="no">Rails</span><span class="o">.</span><span class="n">root</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34;config&#34;</span><span class="p">,</span> <span class="s2">&#34;database_central.yml&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="o">.</span><span class="n">File</span><span class="o">.</span><span class="n">read</span>
</span></span><span class="line"><span class="cl">      <span class="o">.</span><span class="n">ERB</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">      <span class="o">.</span><span class="n">result</span>
</span></span><span class="line"><span class="cl">      <span class="o">.</span><span class="n">YAML</span><span class="o">.</span><span class="n">load</span>
</span></span><span class="line"><span class="cl">      <span class="o">.</span><span class="n">unwrap</span><span class="o">.</span><span class="n">freeze</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">task</span> <span class="ss">:revert_to_original_config</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># reset config variables to original values</span>
</span></span><span class="line"><span class="cl">    <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;SCHEMA&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="vi">@original_config</span><span class="o">[</span><span class="ss">:env_schema</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">config</span> <span class="o">=</span> <span class="vi">@original_config</span><span class="o">[</span><span class="ss">:config</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is how I define a namespace of Rake tasks that start with <code>central:</code>, and every one of those connects to the secondary database as described in <code>database_central.yml</code>. The weird syntax is from my <a href="https://github.com/akitaonrails/chainable_methods"target="_blank" rel="noopener"><code>chainable_methods</code></a>, don&rsquo;t mind it too much.</p>
<p>The <code>db_central/schema.rb</code> is basically a copy of the <code>spec/support/rails_app/db/schema.rb</code> from the central-support gem, with the same tables. The spec runner of both the gem and this secondary app will just load the schema into the test database.</p>
<p>Now that we have the basic underpinnings for specs in place, we can focus on how the application itself can consume those external models.</p>
<p>We start by adding an initializer like <code>config/initializer/db_central.rb</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">DB_CENTRAL</span> <span class="o">=</span> <span class="no">CM</span><span class="p">(</span><span class="no">Rails</span><span class="o">.</span><span class="n">root</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34;config&#34;</span><span class="p">,</span> <span class="s2">&#34;database_central.yml.sample&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">File</span><span class="o">.</span><span class="n">read</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">ERB</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">result</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">YAML</span><span class="o">.</span><span class="n">load</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">unwrap</span><span class="o">.</span><span class="n">freeze</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In this case I am reading from the sample file because, unlike the CI build, when I deploy to Heroku I don&rsquo;t have a script to copy the sample to the final yaml file. This will populate the constant <code>DB_CENTRAL</code> with the database URL stored in the <code>DATABASE_CENTRAL_URL</code> environment variable that I have to set.</p>
<p>Then I create a new file called <code>app/models/remote_application_record.rb</code> that looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">RemoteApplicationRecord</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="n">establish_connection</span> <span class="no">DB_CENTRAL</span><span class="o">[</span><span class="no">Rails</span><span class="o">.</span><span class="n">env</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nb">self</span><span class="o">.</span><span class="n">abstract_class</span> <span class="o">=</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">unless</span> <span class="no">Rails</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">test?</span>
</span></span><span class="line"><span class="cl">    <span class="n">default_scope</span> <span class="o">-&gt;</span> <span class="p">{</span> <span class="n">readonly</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is how you create a new connection pool for the secondary database configuration. You must only have this <code>establish_connection</code> in one place and have the models inherit from here. The <code>abstract_class = true</code> will make ActiveRecord not try to load from a table of the same name as this class.</p>
<p>Then we have a <code>default_scope</code> locking down the model as <code>readonly</code>. We don&rsquo;t want that in the test environment because I still want to have Factory Girl populate the test database with fake data for the specs. But it&rsquo;s a good idea to have it in the production environment just to make sure.</p>
<p>Finally, I can create all the models I need, such as <code>app/models/central/team.rb</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">Central</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">Team</span> <span class="o">&lt;</span> <span class="no">RemoteApplicationRecord</span>
</span></span><span class="line"><span class="cl">    <span class="nb">self</span><span class="o">.</span><span class="n">table_name</span> <span class="o">=</span> <span class="s1">&#39;teams&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kp">include</span> <span class="no">Central</span><span class="o">::</span><span class="no">Support</span><span class="o">::</span><span class="no">TeamConcern</span><span class="o">::</span><span class="no">Associations</span>
</span></span><span class="line"><span class="cl">    <span class="kp">include</span> <span class="no">Central</span><span class="o">::</span><span class="no">Support</span><span class="o">::</span><span class="no">TeamConcern</span><span class="o">::</span><span class="no">Validations</span>
</span></span><span class="line"><span class="cl">    <span class="kp">include</span> <span class="no">Central</span><span class="o">::</span><span class="no">Support</span><span class="o">::</span><span class="no">TeamConcern</span><span class="o">::</span><span class="no">Scopes</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>From here I can just call normal Arel queries such as <code>Central::Team.not_archived.limit(5)</code>.</p>
<h3>Conclusions<span class="hx:absolute hx:-mt-20" id="conclusions"></span>
    <a href="#conclusions" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>If you didn&rsquo;t already, refer to <a href="http://www.akitaonrails.com/en/2016/10/03/compartilhando-models-entre-aplicacoes-rails-parte-1"target="_blank" rel="noopener">Part 1</a> for more details.</p>
<p>This is a simple recipe to share model logic between a main read-write Rails app and a secondary read-only Rails app. They share most (not all) of the same models, they share the same logic (through some of the Concerns), and they share the same database.</p>
<p>In this particular case, the recommended approach is to create a <a href="https://devcenter.heroku.com/articles/heroku-postgres-follower-databases"target="_blank" rel="noopener">Follower database</a>, which is how Heroku calls a secondary replicated database, and make my secondary application connect to that (since it only needs a read-only source).</p>
<p>For more complicated scenarios, you will need a more complicated solution such as an HTTP API layer to make sure only one App manages model migrations and such. But the Rubygem approach should be &ldquo;good enough&rdquo; for many cases.</p>
<p>If I really need to go that way, it won&rsquo;t be too difficult to transform this small gem into a full-blown Rails API app. If you can&rsquo;t even separate the logic as a Concern, you won&rsquo;t be able to separate them as APIs either, so consider this a quick exercise, a first step towards creating an <a href="http://programmers.stackexchange.com/questions/184464/what-is-an-anti-corruption-layer-and-how-is-it-used"target="_blank" rel="noopener">Anti Corruption Layer</a>.</p>
<p>And as a bonus, consider contributing to the <a href="https://github.com/Codeminer42/cm42-central"target="_blank" rel="noopener">Central</a> and <a href="https://github.com/Codeminer42/cm42-central-support"target="_blank" rel="noopener">central-support</a> open source projects. I intend to build a competitive Pivotal Tracker/Trello alternative and we are getting there!</p>
]]></content:encoded><category>rails</category><category>databases</category><category>software-architecture</category></item><item><title>Sharing models between Rails Apps - Part 1</title><link>https://akitaonrails.github.io/en/2016/10/03/sharing-models-between-rails-apps-part-1/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2016/10/03/sharing-models-between-rails-apps-part-1/</guid><pubDate>Mon, 03 Oct 2016 15:00:00 -0300</pubDate><description>&lt;p&gt;&lt;em&gt;&amp;ldquo;Build a microservice and expose an API.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;That would be the quick answer if you ask any developer how to share business logic between applications.&lt;/p&gt;
&lt;p&gt;Although it makes sense in many situations, it&amp;rsquo;s not a good answer all the time.&lt;/p&gt;
&lt;p&gt;My TL;DR for some situations is that you can organize your model logic as &lt;a href="http://api.rubyonrails.org/classes/ActiveSupport/Concern.html"target="_blank" rel="noopener"&gt;ActiveSupport::Concerns&lt;/a&gt; (or plain Ruby Modules if you will) and move them out to a Rubygem that your applications can consume.&lt;/p&gt;</description><content:encoded><![CDATA[<p><em>&ldquo;Build a microservice and expose an API.&rdquo;</em></p>
<p>That would be the quick answer if you ask any developer how to share business logic between applications.</p>
<p>Although it makes sense in many situations, it&rsquo;s not a good answer all the time.</p>
<p>My TL;DR for some situations is that you can organize your model logic as <a href="http://api.rubyonrails.org/classes/ActiveSupport/Concern.html"target="_blank" rel="noopener">ActiveSupport::Concerns</a> (or plain Ruby Modules if you will) and move them out to a Rubygem that your applications can consume.</p>
<p>Notice that I am only speaking of Models, not Controllers or Views. To share those you would need a full-blown Rails Engine instead. But in many cases I&rsquo;ve seen, the goal was to just share the business logic between applications while having separate front-end logic.</p>
<p>A small example of this scenario is the open sourced project I&rsquo;ve been working on in the last few weeks. <a href="https://github.com/Codeminer42/cm42-central"target="_blank" rel="noopener">Central</a>, which is a Pivotal Tracker/Trello alternative - if you&rsquo;re interested.</p>
<p>A few days ago I started a new project (for internal use only) that would query the same models as Central. I didn&rsquo;t want to implement HTTP APIs at this point, and the new application would itself have models with relationships to the models in Central (while treating them as read-only).</p>
<p>After a few refactorings, most of Central&rsquo;s models look like this one:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Team</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">Central</span><span class="o">::</span><span class="no">Support</span><span class="o">::</span><span class="no">TeamConcern</span><span class="o">::</span><span class="no">Associations</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">Central</span><span class="o">::</span><span class="no">Support</span><span class="o">::</span><span class="no">TeamConcern</span><span class="o">::</span><span class="no">Validations</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">Central</span><span class="o">::</span><span class="no">Support</span><span class="o">::</span><span class="no">TeamConcern</span><span class="o">::</span><span class="no">Scopes</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">Central</span><span class="o">::</span><span class="no">Support</span><span class="o">::</span><span class="no">TeamConcern</span><span class="o">::</span><span class="no">DomainValidator</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And I have this dependency in the <code>Gemfile</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;central-support&#39;</span><span class="p">,</span> <span class="ss">github</span><span class="p">:</span> <span class="s1">&#39;Codeminer42/cm42-central-support&#39;</span><span class="p">,</span> <span class="ss">branch</span><span class="p">:</span> <span class="s1">&#39;master&#39;</span><span class="p">,</span> <span class="nb">require</span><span class="p">:</span> <span class="s1">&#39;central/support&#39;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Whenever I change the concerns, I do a <code>bundle update central-support</code> in the projects (this is the one caveat to keep in mind to avoid dealing with outdated models).</p>
<p>This was possible because most of those models were mature and stable and I will not be changing them often. I don&rsquo;t recommend exposing unstable dependencies (as gems or APIs, it doesn&rsquo;t matter), because this is a recipe for huge headaches of cascading breaking changes due to outdated dependencies that are changing too often.</p>
<blockquote>
  <p>You should ONLY expose business logic that is reasonably stable (changes only every week or so).</p>

</blockquote>
<p>The whole endeavor was to build a certain Rubygems structure, organize the original models into Concerns (which breaks no behavior), make sure specs are still passing, and then move the content (models and specs) over to the new Rubygems and make sure the specs pass there.</p>
<p>That&rsquo;s how I built a secondary open source dependency for Central, called <a href="https://github.com/Codeminer42/cm42-central-support"target="_blank" rel="noopener">Central Support</a>. Like many gems, its main file <a href="https://github.com/Codeminer42/cm42-central-support/blob/master/lib/central/support.rb"target="_blank" rel="noopener">lib/central/support.rb</a> is nothing but a bunch of &lsquo;require&rsquo;s to load all the dependencies.</p>
<p>So I methodically organized logic as concerns, such as <a href="https://github.com/Codeminer42/cm42-central-support/blob/master/lib/central/support/concerns/team_concern/associations.rb"target="_blank" rel="noopener">lib/central/support/concerns/team_concern/association.rb</a>, which is just the extraction of the Active Record associations from the &lsquo;Team&rsquo; model.</p>
<p>Cut from Central, Paste into Support. When all relevant logic has been moved, I could move the entire Team model spec, mostly without any changes, and make it run. Every time I moved a piece, I <code>bundle update</code>d the gem and ran the main spec suite to make sure nothing broke.</p>
<p>And this is the difficult part: make a sandbox where those concerns could run and be tested.</p>
<p>To begin, I needed to build a minimal Rails app inside the spec folder, at <code>spec/support/rails_app</code>. And there I could put fake models that include the concerns I had just extracted from Central.</p>
<p>There is scarce documentation on how to do that, but I think you can just do <code>rails new</code> and start from there, or copy my <code>rails_app</code> folder for the bare minimum. My case is simpler because this gem is not meant to be general-purpose, so I don&rsquo;t need to run it against different Rails versions, for example.</p>
<p>This internal test app must have a carefully crafted <a href="https://github.com/Codeminer42/cm42-central-support/blob/master/spec/support/rails_app/Gemfile"target="_blank" rel="noopener"><code>Gemfile</code></a>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;central-support&#39;</span><span class="p">,</span> <span class="ss">path</span><span class="p">:</span> <span class="no">File</span><span class="o">.</span><span class="n">expand_path</span><span class="p">(</span><span class="s2">&#34;../../../..&#34;</span><span class="p">,</span> <span class="bp">__FILE__</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;devise&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;pg&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;friendly_id&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;foreigner&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">group</span> <span class="ss">:test</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;test-unit&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;rspec-rails&#39;</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>You don&rsquo;t have to add the gems from the main <a href="https://github.com/Codeminer42/cm42-central-support/blob/master/central-support.gemspec"target="_blank" rel="noopener">gemspec</a>. But you can remove the development dependencies that you would put in the gemspec and keep them in the test app Gemfile.</p>
<p>Now, from the <a href="https://github.com/Codeminer42/cm42-central-support/blob/master/Gemfile"target="_blank" rel="noopener">main <code>Gemfile</code></a> you can do:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">source</span> <span class="s1">&#39;https://rubygems.org&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">eval_gemfile</span> <span class="no">File</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="no">File</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="bp">__FILE__</span><span class="p">),</span> <span class="s2">&#34;spec/support/rails_app/Gemfile&#34;</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Most tutorials to build a Rubygem will add a line to load dependencies from the gemspec, but here we are replacing it with the test app&rsquo;s Gemfile. This is the manifest that will be loaded when we run <code>bundle exec rspec</code>, for example.</p>
<p>Speaking of which, this is the <a href="https://github.com/Codeminer42/cm42-central-support/blob/master/spec/rails_helper.rb"target="_blank" rel="noopener"><code>spec/rails_helper.rb</code></a>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;RAILS_ENV&#39;</span><span class="o">]</span> <span class="o">||=</span> <span class="s1">&#39;test&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;rails/all&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;factory_girl&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;factory_girl_rails&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;rspec/rails&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;shoulda/matchers&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="sb">`cd spec/support/rails_app ; bin/rails db:drop db:create db:schema:load RAILS_ENV=test`</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;support/rails_app/config/environment&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;support/database_cleaner&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;support/factory_girl&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;support/factories&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;spec_helper&#39;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>To wrap your head around it:</p>
<ul>
<li><code>bundle exec rspec</code> will load the main <code>Gemfile</code></li>
<li>the main <code>Gemfile</code> will load from the internal test app&rsquo;s <code>Gemfile</code></li>
<li>that internal test app&rsquo;s <code>Gemfile</code> will require the gemspec from <code>../../../..</code> and the development and test groups of gems (including Rspec, Factory Girl, etc)</li>
<li>the gemspec will require the runtime dependencies such as &ldquo;activesupport&rdquo;, &ldquo;enumerize&rdquo;, etc</li>
<li>finally, the <code>rails_helper.rb</code> listed above will load.</li>
</ul>
<p>There at line 11, the runner will execute a command to <code>cd</code> into the internal test app&rsquo;s root folder and run the <code>db:schema:load</code>, therefore you need a <code>db/schema.rb</code> ready to load, as well as <code>config/database.yml</code>.</p>
<p>The <a href="https://github.com/Codeminer42/cm42-central-support/blob/master/spec/spec_helper.rb"target="_blank" rel="noopener"><code>spec/spec_helper.rb</code></a> is more standard, with optional configurations for test coverage, etc.</p>
<p>The models inside the internal test app are the important parts, because they are the means to include the extracted concerns into a runnable format. The <a href="https://github.com/Codeminer42/cm42-central-support/blob/master/spec/support/rails_app/app/models/team.rb"target="_blank" rel="noopener">&lsquo;spec/support/rails_app/app/models/team.rb&rsquo;</a> is such an example:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Team</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">Central</span><span class="o">::</span><span class="no">Support</span><span class="o">::</span><span class="no">TeamConcern</span><span class="o">::</span><span class="no">Associations</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">Central</span><span class="o">::</span><span class="no">Support</span><span class="o">::</span><span class="no">TeamConcern</span><span class="o">::</span><span class="no">Validations</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">Central</span><span class="o">::</span><span class="no">Support</span><span class="o">::</span><span class="no">TeamConcern</span><span class="o">::</span><span class="no">Scopes</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">Central</span><span class="o">::</span><span class="no">Support</span><span class="o">::</span><span class="no">TeamConcern</span><span class="o">::</span><span class="no">DomainValidator</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And with that, I could move the unmodified specs directly from the main project (Central), such as <a href="https://github.com/Codeminer42/cm42-central-support/blob/master/spec/central/support/team_spec.rb"target="_blank" rel="noopener"><code>spec/central/support/team_spec.rb</code></a>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;rails_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">describe</span> <span class="no">Team</span><span class="p">,</span> <span class="ss">type</span><span class="p">:</span> <span class="ss">:model</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">it</span> <span class="p">{</span> <span class="n">is_expected</span><span class="o">.</span><span class="n">to</span> <span class="n">validate_presence_of</span> <span class="ss">:name</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">it</span> <span class="p">{</span> <span class="n">is_expected</span><span class="o">.</span><span class="n">to</span> <span class="n">have_many</span> <span class="ss">:enrollments</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">it</span> <span class="p">{</span> <span class="n">is_expected</span><span class="o">.</span><span class="n">to</span> <span class="n">have_many</span> <span class="ss">:users</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">it</span> <span class="p">{</span> <span class="n">is_expected</span><span class="o">.</span><span class="n">to</span> <span class="n">have_many</span> <span class="ss">:ownerships</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">it</span> <span class="p">{</span> <span class="n">is_expected</span><span class="o">.</span><span class="n">to</span> <span class="n">have_many</span> <span class="ss">:projects</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If you go back in the Central project, some commits back, you will find the very same file as <a href="https://github.com/Codeminer42/cm42-central/blob/a80eefadf233f4a8c5f88829836c872b199798cd/spec/models/team_spec.rb"target="_blank" rel="noopener"><code>spec/models/team_spec.rb</code></a>. And the main advantage of this approach is exactly being able to move most of the code out of the main project, together with their specs, into a dependency gem, without having to &ldquo;rewrite&rdquo; anything.</p>
<p>If I had to rewrite all or a big chunk of the code, it would&rsquo;ve been a more expensive choice and I would probably have deferred it to another time and focus on more valuable features first.</p>
<p>This approach is not perfect but it was super cheap. I could move all the relevant business logic out of the main project without having to rewrite anything but a few lines of wiring code. The new dependency gem received all the relevant bits and specs, and everything just runs.</p>
<p>So, if you have 2 or more Rails apps that could share the same models, this is how you can start it. Of course, there are always a lot of caveats to keep in mind.</p>
<p>In my case, the Central project is the one that can read-and-write to the database. My internal secondary app is just using the models as read-only. When 2 different apps write to the same database, you may have a number of conflicts to deal with.</p>
<p>This approach is useful if your secondary application is akin to an Administration dashboard, for example. You need to have some of the same associations, scopes, even validations for eventual editing, but it&rsquo;s limited to a few, controlled users.</p>
<p>This is also useful if you&rsquo;re doing data analysis, and again you can use the same associations, scopes, to build reports and dashboards. Essentially, if you need read-only access, this is a no-brainer.</p>
<p>In the <a href="http://www.akitaonrails.com/en/2016/10/03/compartilhando-models-entre-apps-rails-parte-2"target="_blank" rel="noopener">next article</a> I will explain how I wired a secondary application, using the central-support gem and dealing with 2 different databases at the same time.</p>
]]></content:encoded><category>rails</category><category>software-architecture</category><category>testing</category></item><item><title>The Obligatory "Why Elixir?" Personal Take</title><link>https://akitaonrails.github.io/en/2015/12/01/the-obligatory-why-elixir-personal-take/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/12/01/the-obligatory-why-elixir-personal-take/</guid><pubDate>Tue, 01 Dec 2015 13:26:00 -0200</pubDate><description>&lt;p&gt;So, I&amp;rsquo;ve been studying and exercising quite a bit with Elixir. José Valim recently announced the new features of the upcoming &lt;a href="https://twitter.com/josevalim/status/670595716776116224"target="_blank" rel="noopener"&gt;Elixir 1.2&lt;/a&gt;. The language design is already elegant, lean, and it keeps gradually polishing itself.&lt;/p&gt;
&lt;p&gt;First and foremost, I am a Web Applications Developer. I deal with Ruby on Rails applications and infrastructure. So, I am not a Desktop developer, a Mobile developer, Games developer or a Tools developer. This is very important to get out of the way right from the start.&lt;/p&gt;</description><content:encoded><![CDATA[<p>So, I&rsquo;ve been studying and exercising quite a bit with Elixir. José Valim recently announced the new features of the upcoming <a href="https://twitter.com/josevalim/status/670595716776116224"target="_blank" rel="noopener">Elixir 1.2</a>. The language design is already elegant, lean, and it keeps gradually polishing itself.</p>
<p>First and foremost, I am a Web Applications Developer. I deal with Ruby on Rails applications and infrastructure. So, I am not a Desktop developer, a Mobile developer, Games developer or a Tools developer. This is very important to get out of the way right from the start.</p>
<p>And by the way, as I will focus my programming efforts more and more into Elixir, it does not mean I am &ldquo;switching&rdquo; from Ruby to Elixir. I don&rsquo;t need to make exclusive choices, and in my mind, at least for a period of time, Rails joining forces with Phoenix will be a very difficult combo to beat for my web development strategies.</p>
<p>I know, Phoenix is built like Rails so why not just switch altogether: because most content-based sites don&rsquo;t need the concurrency aspects of Phoenix, and I said &ldquo;for a period of time&rdquo; because Rails still has a humongous ecosystem with more mature gems that make development easier and faster. This can change in the future, but for now the combo makes sense as I can build a normal website in Rails as I would normally do (with Devise, ActiveAdmin, Spree or whatever) and add Phoenix for stuff like WebSockets (real-time notifications, real-time chatting, background jobs that can be run more efficiently than Sidekiq, etc).</p>
<p>This article will summarize my personal take on 2 fronts:</p>
<ul>
<li><a href="#developer-roles">Other Developer Roles than just the Web</a></li>
<li><a href="#functional-concepts">&ldquo;Functional&rdquo; Concepts that Really Matter</a>
<ul>
<li><a href="#immutability">Immutability and Opaque Message Passing are VERY important</a></li>
<li><a href="#coroutines">Coroutines and Schedulers</a></li>
<li><a href="#static-dynamic">Static vs Dynamic Typing is still controversial</a></li>
<li><a href="#fault-tolerance">Fault Tolerance: Don&rsquo;t fear your code</a></li>
</ul>
</li>
<li><a href="#summary">Summary</a></li>
</ul>
<p>As anything that I want to argue about, this is <strong>lengthy</strong>, but it wouldn&rsquo;t be nice to just state something without further elaboration. In this quest for understanding, I may have confused a bit or two, so let me know in the comments section below if there are things to fix or to explain in more detail.</p>
<p>Let&rsquo;s get started.</p>
<p><a name="developer-roles"></a></p>
<h3>Other Developer Roles than just the Web<span class="hx:absolute hx:-mt-20" id="other-developer-roles-than-just-the-web"></span>
    <a href="#other-developer-roles-than-just-the-web" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>In a Desktop environment, you will definitely want to make a combination of <a href="https://github.com/nwjs/nw.js/"target="_blank" rel="noopener">Node-Webkit</a> with native libraries. If you&rsquo;re in specific corporate environments, you won&rsquo;t have any choices other than plain WFC based .NET development or Java Swing. Your options have been set for quite some time, and even Visual Basic.NET still has its place. Specific toolchains will be dictated by Microsoft and Oracle/Java Community Process.</p>
<p>In Linux environments you will still use wrappers around GTK+, Qt or similar toolkits. There aren&rsquo;t a lot of ways around this.</p>
<p>If you&rsquo;re a Mobile-first developer, you <strong>do</strong> need to learn your way into Swift and Objective-C (to some extent) for iOS and the specific Java flavor for Android&rsquo;s Dalvik/ART. But I will argue that you have a lot to gain in native development if you use <a href="http://www.rubymotion.com/"target="_blank" rel="noopener"><strong>RubyMotion</strong></a>. Or, you can simply keep following Facebook&rsquo;s <a href="https://facebook.github.io/react-native/"target="_blank" rel="noopener">React Native</a> endeavor. There&rsquo;s a lot of fragmentation in this environment, you can do as little as possible with Web Mobile and Phonegap/Cordova, and build compelling apps with tools such as Ionic Framework. The only consensus is that if you really want to build the advanced next gen stuff, you want to dive deep into the native frameworks each platform has to offer.</p>
<p>If you&rsquo;re a Game Developer you want to be as close to the metal as possible. It&rsquo;s definitely feasible to write perfectly playable mobile games using any number of Javascript libraries together with HTML 5&rsquo;s technologies such as the Canvas and WebGL. But for the next gen blockbuster you will either use mature engines such as Unity or Unreal or even build your own if you&rsquo;re really invested in this field. This will require you to actually know your way into C/C++ programming. Really depends on how deep you want to go down the rabbit hole.</p>
<p>If you&rsquo;re a Tools Developer you will have the benefits from both old and new generation of languages. If you&rsquo;re closer to the Linux Kernel you will really need C/C++ in your baggage. But for the new generation of light containers (LXC), Docker, you can benefit from <strong>Go</strong>, an application development environment suited to make life easier than having to handle C/C++ idiosyncrasies. <strong>Rust</strong> is another great new choice to make it easier to - among other things - write memory-leak free small libraries and tools (I am mentioning that in particular because it&rsquo;s important for languages like Ruby or Python to be able to add performance by binding to C-based native libraries and Rust makes this task easier).</p>
<p>Different languages have different teams and different long term goals, which is why it&rsquo;s not apples and oranges to compare languages. Go, for instance, is heavier than Rust, but both are good for command-line tools, specialized daemons, and in the case of Go, networking-heavy and concurrency-heavy endeavors.</p>
<p>In my eyes, Go is a &ldquo;better&rdquo; Java or C++. And don&rsquo;t interpret this wrong: Java is still a very fine language and platform. There is hardly anything that comes close to the maturity of the JVM and the extensive ecosystem behind it. I would not think for a second of trying to rewrite complex systems written in Java such as the Lucene library or Elasticsearch/SOLR solutions, for example.</p>
<p>But the power of Standard Java is difficult to unleash without some warming up for the HotSpot to pick up steam. It makes it not a great solution for command-line tools. But now you have a good middle ground with Go. You also don&rsquo;t have a good time embedding Java into other platforms, and then you would need to go back to C, but now you have another good middle ground with Rust.</p>
<p>If you want to unleash different programming models, specially those more suited to concurrent abstractions such as the <a href="https://en.wikipedia.org/wiki/Actor_model_and_process_calculi_history"target="_blank" rel="noopener">Hoare&rsquo;s CSP</a> like Actor model, you can try <strong>Scala</strong> with <a href="http://akka.io/"target="_blank" rel="noopener">Akka</a> (which is now the standard actor library) and <strong>Clojure</strong>&rsquo;s <a href="http://blog.paralleluniverse.co/2013/05/02/quasar-pulsar/"target="_blank" rel="noopener">Pulsar/Quasar</a>. Akka and Quasar are the ones that come &ldquo;close&rdquo; (but can never match) Erlang&rsquo;s built-in OTP platform.</p>
<p>For the Web at large, you can do just fine with the current (ever changing, unstable) Node.js ecosystem, Python (Django, Plone), Ruby (Ruby on Rails, Sinatra), PHP (Zend, Laravel), even Perl has its place. Combined with mature services in different platforms (Elasticsearch in Java, PostgreSQL in C, RabbitMQ in Erlang), any big and complex Web Application can be written with any number of best-of-breed tools and combinations that best suit your needs.</p>
<p>This is an unfair, short overview, of course. I didn&rsquo;t cover every aspect of computer science or industry. There are several other active and useful languages such as Lua, Haskell, Fortran, Ada, Julia, R. The message being: you don&rsquo;t need to choose a single language, it will really depend on what you&rsquo;re going to deliver. And a true craftsman will master many tools to get the job done in the best way possible.</p>
<p><a name="functional-concepts"></a></p>
<h3>&ldquo;Functional&rdquo; Concepts that Really Matter<span class="hx:absolute hx:-mt-20" id="functional-concepts-that-really-matter"></span>
    <a href="#functional-concepts-that-really-matter" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><a href="http://www.akitaonrails.com/2015/10/28/personal-thoughts-on-the-current-functional-programming-bandwagon"target="_blank" rel="noopener">I wrote about my opinions</a> on the current surge in the so called Functional style of programming. I recommend you read it before continuing.</p>
<p>There are some aspects that are adamant if you really want to go beyond the academic research and into the real world of productivity.</p>
<p><a name="immutability"></a></p>
<h4>Immutability and Opaque Message Passing are VERY important<span class="hx:absolute hx:-mt-20" id="immutability-and-opaque-message-passing-are-very-important"></span>
    <a href="#immutability-and-opaque-message-passing-are-very-important" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>In order for computation to be fast, we are used to share data between routines. We move pointers around and change data in place.</p>
<p>It&rsquo;s not particularly fast to make things immutable and not shared. The more you make data mutable and the more you share, the harder it is to make your code run concurrently.</p>
<p>It is an important <strong>trade-off</strong>: if you see mutable data and shared state, you&rsquo;re optimizing for performance.</p>
<p>One can&rsquo;t say that a language is essentially good or bad for having mutable or immutable data. But for what it&rsquo;s worth, my personal opinion is that it&rsquo;s harder to convince users to follow conventions like <em>&ldquo;share a little as possible, mutate as little as possible.&rdquo;</em> Most will not even know about it if it&rsquo;s not built-in and enforced. I prefer having immutability enforced by default.</p>
<p>In Erlang, data is <strong>immutable</strong>. Similar to Java it <a href="http://erlang.org/pipermail/erlang-questions/2013-March/072760.html"target="_blank" rel="noopener">passes values by reference</a> in routine calls (it is not copying values between calls, as many misunderstand it).</p>
<p>And in the case of recursion it optimizes through <a href="http://learnyousomeerlang.com/recursion"target="_blank" rel="noopener">Tail Call Optimization</a> to make it faster. By the way, this is one optimization that the Java VM just can&rsquo;t quite do yet. Clojure needs special &lsquo;recur&rsquo; and &rsquo;trampoline&rsquo; calls, for example. Scala can rewrite the tail recursion to a loop in compile time, with the &lsquo;@tailrec&rsquo; annotation. <a href="http://ferd.ca/erlang-s-tail-recursion-is-not-a-silver-bullet.html"target="_blank" rel="noopener">Erlang has its own traps</a> as well, so not so black and white at the moment.</p>
<p>In Erlang, as I explained before, you run functions within completely isolated processes. If the function recurs or blocks, it stays isolated. Processes can only communicate by sending (immutable and opaque) messages to each other. Messages get queued in a &ldquo;run queue&rdquo; or &ldquo;mailbox&rdquo; and the function can choose to receive and respond to those messages. That&rsquo;s it.</p>
<p>So, you can pass values by reference between routines, or you can share data in a third party process as the mediator of data. One such infrastructure built-in to Erlang is the <strong>ETS</strong>, the <a href="http://elixir-lang.org/getting-started/mix-otp/ets.html"target="_blank" rel="noopener">Erlang Term Storage</a>, which is part of the so called OTP platform. Think of ETS as a very simple and very fast built-in key value storage like Memcached. You use it for the same use cases as a cache and it&rsquo;s as simple as just doing this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">table</span> <span class="o">=</span> <span class="ss">:ets</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">:my_fancy_cache</span><span class="p">,</span> <span class="o">[</span><span class="ss">:set</span><span class="p">,</span> <span class="ss">:protected</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="ss">:ets</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="n">table</span><span class="p">,</span> <span class="p">{</span><span class="s2">&#34;some_key&#34;</span><span class="p">,</span> <span class="n">some_value</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="ss">:ets</span><span class="o">.</span><span class="n">lookup</span><span class="p">(</span><span class="n">table</span><span class="p">,</span> <span class="s2">&#34;some_key&#34;</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Many might argue that Erlang&rsquo;s <strong>rigid process isolation</strong> and communication strictly restricted to opaque message passing are overkill and that you can pass by using something akin to Clojure&rsquo;s <a href="http://clojure.org/refs"target="_blank" rel="noopener">MVCC Software Transaction Memory</a>, or STM. You do have STM in Erlang, with the other built-in OTP tool, built on top of the ETS, called <strong>Mnesia</strong>. It offers the equivalent of ACID database transactions in-memory. It&rsquo;s not a new concept, but STM is not available as a language feature and it&rsquo;s still uncertain if it really is a good choice to have it.</p>
<p>An inspired result, I believe, from Clojure&rsquo;s choice of having transactional memory with history queue and snapshot isolation is shown in its crown jewel, <a href="http://www.datomic.com/"target="_blank" rel="noopener">Datomic</a>. The idea is not revolutionary by any stretch of the imagination as you have many other <a href="http://www.xaprb.com/blog/2013/12/28/immutability-mvcc-and-garbage-collection/"target="_blank" rel="noopener">prior art</a> such as RethinkDB, CouchDB, and extensions for existing databases. Good for a service, still I don&rsquo;t think it&rsquo;s a good thing to share state, even if you have a transactor around that state. Erlang&rsquo;s immutability with rigid process isolation still has no match.</p>
<p><a name="coroutines"></a></p>
<h4>Coroutines and Schedulers<span class="hx:absolute hx:-mt-20" id="coroutines-and-schedulers"></span>
    <a href="#coroutines-and-schedulers" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>You already know sub-routines, you do it all the time by partitioning large portions of code into smaller functions or methods that call each other. You may already know about a specialized kind of Coroutines in the form of <a href="http://bit.ly/1lVuLFJ"target="_blank" rel="noopener">Fibers</a> (as first implemented in Windows circa 1997).</p>
<p>Fibers offer a way for your current function execution to &ldquo;yield&rdquo; back to its caller, preserving its current state, and then the caller can &ldquo;resume&rdquo; the suspended Fiber to continue its execution from where it last yielded. This allows for non-preempted, cooperative multitasking. We have Fibers in Python, Ruby, and other languages and it allows the creation of constructions like Generators. Even Javascript can have some form of Fibers if you add libraries like <a href="https://github.com/laverdet/node-fibers"target="_blank" rel="noopener">node-fibers</a>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">Fiber</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;fibers&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">sleep</span><span class="p">(</span><span class="nx">ms</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">fiber</span> <span class="o">=</span> <span class="nx">Fiber</span><span class="p">.</span><span class="nx">current</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">fiber</span><span class="p">.</span><span class="nx">run</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span> <span class="nx">ms</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Fiber</span><span class="p">.</span><span class="k">yield</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The call to &lsquo;yield&rsquo; suspends the current execution until the function in the &lsquo;setTimeout&rsquo; is called. Then it calls &lsquo;run&rsquo;, which resumes the previously yielded function. This is still &ldquo;rudimentary&rdquo; compared to coroutines: because the function itself has to yield control out to the reactor event loop, in the case of a Node.js application. If you don&rsquo;t, you will block the event loop in a single threaded Node.js process, and therefore you block everything until the function finishes, defeating the whole purpose. And this is one of those &ldquo;conventions&rdquo; that &ldquo;good&rdquo; programmers should follow, but most will forget.</p>
<p>Fibers are useful to make it less ugly to program in a rudimentary Reactor environment, where you depend on callbacks calling callbacks and you end up with the anti-pattern of <a href="https://bjouhier.wordpress.com/2012/03/11/fibers-and-threads-in-node-js-what-for/"target="_blank" rel="noopener">callback pyramid of doom</a>. With Fibers you can program as you would in a synchronous imperative language transforming this ugly Javascript code:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">archiveOrders</span><span class="p">(</span><span class="nx">date</span><span class="p">,</span> <span class="nx">cb</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">db</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">conn</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="k">return</span> <span class="nx">cb</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">conn</span><span class="p">.</span><span class="nx">query</span><span class="p">(</span><span class="s2">&#34;selectom orders where date &lt; ?&#34;</span><span class="p">,</span>  
</span></span><span class="line"><span class="cl">               <span class="p">[</span><span class="nx">date</span><span class="p">],</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">orders</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="k">return</span> <span class="nx">cb</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="nx">helper</span><span class="p">.</span><span class="nx">each</span><span class="p">(</span><span class="nx">orders</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">order</span><span class="p">,</span> <span class="nx">next</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">conn</span><span class="p">.</span><span class="nx">execute</span><span class="p">(</span><span class="s2">&#34;insert into archivedOrders ...&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                     <span class="p">[</span><span class="nx">order</span><span class="p">.</span><span class="nx">id</span><span class="p">,</span> <span class="p">...],</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="k">return</span> <span class="nx">cb</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="nx">conn</span><span class="p">.</span><span class="nx">execute</span><span class="p">(</span><span class="s2">&#34;delete from orders where id=?&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                       <span class="p">[</span><span class="nx">order</span><span class="p">.</span><span class="nx">id</span><span class="p">],</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="k">return</span> <span class="nx">cb</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nx">next</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">          <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;orders been archived&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nx">cb</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">      <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Into this more manageable thing:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">archiveOrders</span> <span class="o">=</span> <span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">date</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">conn</span> <span class="o">=</span> <span class="nx">db</span><span class="p">.</span><span class="nx">connect</span><span class="p">().</span><span class="nx">wait</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="nx">conn</span><span class="p">.</span><span class="nx">query</span><span class="p">(</span><span class="s2">&#34;selectom orders where date &lt; ?&#34;</span><span class="p">,</span>  
</span></span><span class="line"><span class="cl">             <span class="p">[</span><span class="nx">date</span><span class="p">]).</span><span class="nx">wait</span><span class="p">().</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">order</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">conn</span><span class="p">.</span><span class="nx">execute</span><span class="p">(</span><span class="s2">&#34;insert into archivedOrders ...&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                 <span class="p">[</span><span class="nx">order</span><span class="p">.</span><span class="nx">id</span><span class="p">,</span> <span class="p">...]).</span><span class="nx">wait</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nx">conn</span><span class="p">.</span><span class="nx">execute</span><span class="p">(</span><span class="s2">&#34;delete from orders where id=?&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                 <span class="p">[</span><span class="nx">order</span><span class="p">.</span><span class="nx">id</span><span class="p">]).</span><span class="nx">wait</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;orders been archived&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}).</span><span class="nx">future</span><span class="p">();</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The entire <a href="https://en.wikipedia.org/wiki/Futures_and_promises"target="_blank" rel="noopener">Promises, Futures</a> debacle depends in part on proper Fibers. Javascript, being a very poor design, does not come with anything built-in and hence the proliferation of Fibers, Deferreds, Promises, Futures implementations that can never reach neither consensus nor people actually using them at large.</p>
<p>So, Fibers are ok. Coroutines are better because you have multiple points of suspending a function and more. And even better, in Erlang one doesn&rsquo;t need to even think about rudimentary Reactor loops (yes, reactors are a rudimentary construction for concurrency when you have no other good choice): it has <strong>transparent asynchronous calls</strong>. Everything in Erlang is asynchronous and non-blocking but you don&rsquo;t deal with callback pyramids because there is something better underneath: the <strong>Scheduler</strong>.</p>
<p>By the way, I find it very frustrating that <a href="http://stackoverflow.com/questions/18058164/is-golang-goroutine-a-coroutine"target="_blank" rel="noopener">Go&rsquo;s &ldquo;goroutines&rdquo; <strong>are not</strong> proper &ldquo;coroutines&rdquo;</a>.</p>
<p>For each Erlang process with SMP (symmetric multiprocessing) support there will be <strong>one</strong> real thread per CPU core available in your system, and for each thread there will be one single Scheduler to manage the internal green threads (processes) and run-queue.</p>
<p>As a programmer, I don&rsquo;t have to &ldquo;remember&rdquo; to yield control back to a passive event loop. The scheduler will take care of balancing computation time to each concurrent process. If a process is taking too long the Scheduler can choose to suspend it and give time to other routines. Erlang defines a <a href="http://erlang.org/pipermail/erlang-questions/2001-April/003132.html"target="_blank" rel="noopener">&ldquo;reduction&rdquo;</a> and that there are different priority levels of functions. If a function takes more than 2,000 reductions, the Scheduler can choose to suspend it. If you have 8 CPU cores, but the computation on the processes are not heavy, the VM can choose to just use 1 or 2 Schedulers and leave the other 6 idle so the hardware can turn the cores off to save energy (!!). Yep, Erlang is even Eco Friendly!</p>
<p>And we need to repeat this again: because each process is <strong>rigidly isolated</strong>, with immutable data and no shared state, it&rsquo;s easier to suspend a running process. In the case of the JVM this is usually implemented by raising checked exceptions and have everybody implement some Suspendable interface. It can be done using this <a href="http://www.matthiasmann.de/content/view/24/26/"target="_blank" rel="noopener">3rd party Java continuation library</a> where you yield by raising an Exception (!) Nasty stuff.</p>
<p>Rust is <a href="https://github.com/rustcc/coroutine-rs"target="_blank" rel="noopener">still implementing</a> something for coroutines as well, but still nothing as mature. But again, coroutines is just part of the story, you would need a heavier system with userland schedulers for it to make sense. Go is a better candidate to incorporate such a system in its runtime, but it also comes up short on being able to implement all this. There is <a href="http://www.jerf.org/iri/post/2930"target="_blank" rel="noopener">Suture</a>, an attempt to have some of OTP in Go, but it can&rsquo;t be done. Even Akka, the first mainstream OTP clone for Scala, can&rsquo;t come close because of the JVM shortcomings. <a href="http://blog.paralleluniverse.co/2013/05/02/quasar-pulsar/"target="_blank" rel="noopener">Clojure with Pulsar/Quasar</a>, comes <strong>closer</strong>, but is not there yet.</p>
<p>Now, the Erlang Scheduler is not only capable of suspending and resuming processes but it also takes care of message passing between them. So each Scheduler has its own run-queue to queue and dispatch messages. Again, because data is immutable, you only need some form of locking when you want another Scheduler (in another real thread) to take over a few processes in order to balance processing between cores. Erlang has supported SMP <a href="http://erlang.org/pipermail/erlang-questions/2008-September/038231.html"target="_blank" rel="noopener">since OTP R12B</a> (we are at R18 right now, and still evolving).</p>
<p>Most languages still rely on the OS real threads preemptive model to do multitasking. And this is heavy and slow because of all the context switching involved and all the locking logic most programmers will do wrong (the best practice for concurrency is: do not use threads, chances are you will screw up). Again, we make the right assumptions first: programmers can&rsquo;t do proper multithreading, so let a Scheduler do it, when necessary, and avoiding slow OS context switching as much as possible. Suspendable green threads combined with a userland Scheduler coordinating cooperative switching is a way faster and safer choice.</p>
<p>If you want to learn more about coroutines this <a href="http://www.inf.puc-rio.br/~roberto/docs/corosblp.pdf"target="_blank" rel="noopener">Lua Paper</a> on the subject explains in more details what I just elaborated:</p>
<blockquote>
Implementing a multitasking application with Lua coroutines is straightforward. Concurrent tasks can be modeled by Lua coroutines. When a new task is created, it is inserted in a list of live tasks. A simple task dispatcher can be implemented by a loop that continuously iterates on this list, resuming the live tasks and removing the ones that have finished their work (this condition can be signalled by a predefined value returned by the coroutine main function to the dispatcher). Occasional fairness problems, which are easy to identify, can be solved by adding suspension requests in time-consuming tasks.
</blockquote>
<p><a name="static-dynamic"></a></p>
<h4>Static vs Dynamic Typing is still controversial<span class="hx:absolute hx:-mt-20" id="static-vs-dynamic-typing-is-still-controversial"></span>
    <a href="#static-vs-dynamic-typing-is-still-controversial" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>We saw a heavier movement from bureaucratic static typed systems (mainly Java prior to 6, C# prior to 4, C++) to purely dynamic languages such as Perl in the late 80&rsquo;s, Python in the late 90&rsquo;s, Ruby in mid 2000&rsquo;s. We tried to go from <em>&ldquo;making the compiler happy&rdquo;</em> to <em>&ldquo;making programmers happy&rdquo;</em>, which makes more sense if you ask me.</p>
<p>Scala, Groovy, Haskell, Swift brought a very practical middle ground with <a href="https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system"target="_blank" rel="noopener">Hindley-Milner</a> derived <strong>Type Inference</strong> systems, in a way that we can code kinda like in dynamic languages but with the compiler doing more work to infer types for us before generating the final executable bytecode.</p>
<p>But there is a <strong>big</strong> catch: it&rsquo;s very difficult to <strong>hot swap</strong> code inside the runtime if you have static signatures. I&rsquo;m not saying that it is impossible, but a lot more difficult. You can reload code in Java (one example being <a href="https://github.com/spring-projects/spring-loaded"target="_blank" rel="noopener">Spring Loaded</a>) or Haskell (there is a hotswap plugin and other alternatives). You don&rsquo;t do granular reloads in a statically typed language because if you want to change the signature of a method, you have to change the graph that depends on that signature. It&rsquo;s doable, albeit, cumbersome.</p>
<p>In Erlang, because there is no such hard dependencies, and again because of inherent advantages of it only having immutable data with no shared state, and dependencies limited to opaque message passing, you can granularly reload one single module and most important of all: you can implement simple callbacks to <strong>transform the old state of a process into a new state structure</strong>, because just reloading the code is half the story if you will have hundreds of old processes restarting into the new code but having to deal with previous state. In an Erlang GenServer, you just implement this single callback:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">code_change</span><span class="p">(</span><span class="no">OldVersion</span><span class="p">,</span> <span class="no">CurrentState</span><span class="p">,</span> <span class="n">_Extra</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">{</span><span class="n">ok</span><span class="p">,</span> <span class="no">NewState</span><span class="p">}</span><span class="o">.</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>So, while Type Inference is a nice middle ground, the flexibility of Dynamic Typing goes beyond being easy for programmers to use. As with Python, Ruby, Javascript, Perl and other dynamic typed languages, you will want to cover your code with proper test suites - which should not be optional in strong typed languages anyway. There is no doubt a compiler&rsquo;s static analysis helps a lot, but it&rsquo;s my personal take that dynamic typing allows me more flexibility.</p>
<p><a name="fault-tolerance"></a></p>
<h3>Fault Tolerance: Don&rsquo;t fear your code<span class="hx:absolute hx:-mt-20" id="fault-tolerance-dont-fear-your-code"></span>
    <a href="#fault-tolerance-dont-fear-your-code" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>With Dynamic Typing we will end up again in the endless discussion of <em>&ldquo;programmers never do it right, we need a compiler to enforce static rules&rdquo;</em>. And you are almost right: programmers screw up, but a compiler will not save your day anyway, and worst: it might give you a false sense of security. There is no worse security hole than false sense of security. A test suite is way better to assert proper implementation, and it&rsquo;s still no hard guarantee.</p>
<p>I said that this is controversial, as a more rigid static typing system is more productive than having to unit test every input and output types of every function. But if you thought that, you&rsquo;re at least wrong in that this is not what unit tests are for: it&rsquo;s for testing unit behaviors, in spite of what the types are. Testing types is what we call &ldquo;chicken typing&rdquo; and it&rsquo;s another form of defensive programming. You must test for behavior, not for stuff that a compiler would check. And again, having the static typing hinges my flexibility because now I have to constantly fight the typing, add more boilerplate, and ultimately if the behavior is wrong, the code is wrong, despite the type checks.</p>
<p>For a system to be <strong>&ldquo;tolerant to buggy code or uncaught failures&rdquo;</strong> is the opposite of littering your code with guarding statements like &ldquo;try/catch&rdquo; or &ldquo;if err == x&rdquo; or having some form static level checking with Result monads (that most people will just unwrap anyway). Guards can only go so far. And yes, this is anecdotal as there are no statistics that &ldquo;everybody will unwrap&rdquo; and most good programmers won&rsquo;t, but if experience tells me one thing, it&rsquo;s that bad programmers will fall for &ldquo;try/catch&rdquo; the whole thing when they don&rsquo;t know what to do anyway.</p>
<p>You need a system that allows any buggy code to fail and not crash the environment around it, which would bring it into an inconsistent, corrupt, state.</p>
<p>The problem with faulty code is that it leaves the state in a position where it has nowhere else to go. And if this state is shared, you leave every other bit of code in a position where they can&rsquo;t decide where to go next. You will need to shut everything down and restart from the last known good state. Almost all &ldquo;continuous delivery&rdquo; workflows are implemented around restarting everything.</p>
<p>Instead of having to bring everything down in case you forget to try/catch something, you can rely on Erlang&rsquo;s underpinnings of not sharing state, having immutable data, and - most importantly - having the rigidly isolated lightweight process system to make the process that is holding the faulty code to <strong>shut down and warn its Supervisor</strong>. This is the general idea behind the so called OTP in Erlang.</p>
<p>The idea of a Supervisor is to have a small process that monitors other processes. The Supervisor has as little code as possible in order to rarely (or never) fail (the best kind of code is &ldquo;no code&rdquo;). Once a faulty process crashes because of uncaught exception or other undeterministic reasons, it sends a notification message to the Supervisor run-queue mailbox, and then die cleanly. The Supervisor then chooses what to do based on its underlying restart strategy.</p>
<p>Let&rsquo;s say you have a list of URLs you&rsquo;re scraping. But you didn&rsquo;t anticipate dirty structures in your parsing logic. The process doing the scraping crashes and dies. The Supervisor is notified and chooses to restart the process, giving the new process the previous state - the URL list - and now that the faulty URL is not there anymore, the new process can happily continue the job with the next URL in the list.</p>
<p>This is a simple example of the dynamic between the Erlang VM, the Supervisor and its children workers. You can go further and have several Applications, which in turn start up new Supervisors, which in turn start up children processes. And you have a Supervisor Tree that can trap exits and restart granular bits of your runtime code without bringing the rest of the system into an inconsistent state.</p>
<p>Such is the beauty of the rigid process isolation concept.</p>
<p>Every I/O in the system is wrapped in <a href="http://www.erlang.org/doc/tutorial/c_port.html"target="_blank" rel="noopener">Ports</a>, which obey Async/callback logic transparently without you having to create pyramids of callbacks. The process consuming such ports just gets suspended by the Scheduler until the Async call returns and it can resume work. No callback pyramid hell. No need for rudimentary Fiber implementations to allow for rudimentary Promises/Futures systems. Just coroutines inside processes that can be suspended and resumed by the Scheduler. Fewer opportunities for programmer errors to stack up.</p>
<p>So, a programmer will forget to code every possible branch of execution and because he knows that, the <strong>very worst thing</strong> that can happen is not if he forgets an uncaught exception, but if he decides to program <strong>defensively</strong> and add general conditions to trap <strong>any</strong> error and never raise it. You&rsquo;ve seen it before, when you find code that is trapped inside generic try/catch blocks, trying to avoid every error possible. But what really happens is that the system may not crash but your logic and your processing is faulty at its core. And you won&rsquo;t find out about it, because it doesn&rsquo;t crash, therefore no one is ever notified! You will not end up with fewer bugs, you will end up with a mountain of logic bugs that are never noticed because they are all swallowed!!</p>
<p>This is the core of Fault Tolerance: do not fear Erlang, fear the programmers! Instead, we should do what Joe Armstrong presented in his seminal paper <a href="http://www.erlang.org/download/armstrong_thesis_2003.pdf"target="_blank" rel="noopener">&ldquo;Making reliable distributed systems in the presence of software errors&rdquo;</a>. This is both a detailed guide into Erlang and OTP but also his arguments on how to write fault tolerant systems by making the programmer write code as clearly as he originally intended, without the need to be defensive, with the confidence that if he misses something, OTP will be there to catch it and not let the system die, but instead to give a chance to fix it and reload it without disrupting other good parts of the system.</p>
<p>This is the ultimate goal of good programming: not being defensive, not putting try/catch everywhere because you&rsquo;re afraid.</p>
<p><a name="summary"></a></p>
<h3>Summary<span class="hx:absolute hx:-mt-20" id="summary"></span>
    <a href="#summary" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>This short summary is the reason why Erlang sounds very compelling for my Web Development needs, or for any scalable complex system, at least for me.</p>
<p>Every other language had to make trade-offs. Every new language on top of the JVM has to deal with limitations inherent to how the JVM was originally architected. Rust still can build better abstractions, but its scope is for smaller tools and libraries, not distributed complex systems. Yes, eventually it can do whatever C/C++ can do, and Mozilla is actually basing the core of its next generation browser core on Rust. This will bootstrap better higher level libraries and frameworks, the same way Apple using Objective-C created the entire set of Core frameworks that make implementing complex applications much easier.</p>
<p>Go made a choice for keeping familiarity with its C++ inheritance. Of course, it has tons of useful features, in particular the built-in goroutines that make concurrent coding much easier than in previous languages.</p>
<p>Haskell is too strict for most programmers (yes, Monads, still difficult for the average programmer to fully grasp) and despite contrary opinions, to me it still feels like it appeals more to researchers than everyday developers. Other dynamic languages such as Ruby, Python, may still go the way of Erlang with Ruby adding some immutability (<a href="https://bugs.ruby-lang.org/issues/11473"target="_blank" rel="noopener">in 2.3</a> with immutable String by default), but it&rsquo;s still a long way to go.</p>
<p>Erlang has everything, as Joe Armstrong envisioned a fault tolerant system to be. It started as an exercise implemented in Prolog in 1986. It migrated from the previous JAM compiler to the current BEAM VM in 1998. It added SMP around 2008. It has been gradually evolving, polishing its rough edges, being really battle tested in really mission critical systems for decades. It&rsquo;s ready for us, right now.</p>
<p>It has Fault Tolerance guaranteed by the principles of immutable data, no shared state, pure opaque message passing, and suspendable processes, all managed by Schedulers. This guarantees that faulty routines can crash one single process but not the entire system and definitely not bringing state of other process to inconsistent, corrupted, states.</p>
<p>Then you can instrument your virtual machine and check that Supervisors are restarting children more than you want and decide to fix the buggy code. And once you do, you can choose <strong>not</strong> to shut down and restart the entire system to reload the code fixes, you can do it granularly, on the fly, with running processes that will pick up the fixes once the Supervisor restarts it. The granular hot swap of code is guaranteed because there is no hierarchy of types to care about.</p>
<p>And because you have proper coroutines with no hierarchy of shared state dependencies, you can have <strong>asynchronous exceptions</strong> that can forcefully shut down processes without creating side effects to other running processes. A Supervisor may choose to restart its entire children list when one of its children crashes and another of its children depended on that previous process. You can switch from &ldquo;one for one&rdquo; restart strategy to &ldquo;one for all&rdquo; (like the Musketeers).</p>
<p>There is only one problem with Erlang: it was not designed for &ldquo;programmer happiness&rdquo;, a concept we&rsquo;ve come to take for granted because of Ruby and newer languages.</p>
<p>Erlang has its roots in Prolog and it shows. Once you step up and really dive deep exercising with the language you can possibly get used to it. But if you came from more modern dynamic languages such as Ruby, Python, Groovy, you will definitely miss the comfortable modern constructions.</p>
<p>Elixir is the missing piece, the Philosopher&rsquo;s Stone if you will, that can unlock all the 30 years of refinements, maturity, industry battle tested technologies in large scales, to the average programmer.</p>
<p>It brings many modern constructs such as making macros easier in order to allow for Domain Specific Languages, having testable comments in your code, adding a more modern standard library that is easily recognizable from a Ruby or Clojure point of view, polymorphism through Protocols, and so on.</p>
<p>This is one example of Elixir straight from its source code tests:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Code</span><span class="o">.</span><span class="n">require_file</span> <span class="s2">&#34;../test_helper.exs&#34;</span><span class="p">,</span> <span class="n">__DIR__</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Mix</span><span class="o">.</span><span class="n">ArchiveTest</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">MixTest</span><span class="o">.</span><span class="n">Case</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">doctest</span> <span class="no">Mix</span><span class="o">.</span><span class="n">Archive</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;archive&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">in_fixture</span> <span class="s2">&#34;archive&#34;</span><span class="p">,</span> <span class="n">fn</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="no">File</span><span class="o">.</span><span class="n">write</span> <span class="s2">&#34;.elixir&#34;</span><span class="p">,</span> <span class="s2">&#34;~&gt; 1.0.0&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="no">Mix</span><span class="o">.</span><span class="n">Archive</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="s2">&#34;.&#34;</span><span class="p">,</span> <span class="s2">&#34;sample.ez&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">archive</span> <span class="o">=</span> <span class="s1">&#39;sample.ez&#39;</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert</span> <span class="no">File</span><span class="o">.</span><span class="n">exists?</span><span class="p">(</span><span class="n">archive</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert</span> <span class="n">has_zip_file?</span><span class="p">(</span><span class="n">archive</span><span class="p">,</span> <span class="s1">&#39;sample/.elixir&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert</span> <span class="n">has_zip_file?</span><span class="p">(</span><span class="n">archive</span><span class="p">,</span> <span class="s1">&#39;sample/priv/not_really_an.so&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert</span> <span class="n">has_zip_file?</span><span class="p">(</span><span class="n">archive</span><span class="p">,</span> <span class="s1">&#39;sample/ebin/Elixir.Mix.Tasks.Local.Sample.beam&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert</span> <span class="n">has_zip_file?</span><span class="p">(</span><span class="n">archive</span><span class="p">,</span> <span class="s1">&#39;sample/ebin/local_sample.app&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">has_zip_file?</span><span class="p">(</span><span class="n">archive</span><span class="p">,</span> <span class="nb">name</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">files</span><span class="p">}</span> <span class="o">=</span> <span class="ss">:zip</span><span class="o">.</span><span class="n">list_dir</span><span class="p">(</span><span class="n">archive</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">Enum</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">files</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">match?</span><span class="p">({</span><span class="ss">:zip_file</span><span class="p">,</span> <span class="o">^</span><span class="nb">name</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">},</span> <span class="o">&amp;</span><span class="mi">1</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Code that can be written like this automatically makes me grin.</p>
<p>This is the perfect combination. Again, it is not without shortcomings. Erlang is by no means a fast language. It is way faster than Ruby, Python, or other interpreted languages. It can be made a bit faster with native compilation through the HIPE compiler, but still nowhere near the speeds of Go, Rust, or any Java derivative such as Scala, Groovy or Clojure.</p>
<p>So, if you really need raw power of computation, you will want Go or Java. Again, I&rsquo;m not saying there are only those 2, but it&rsquo;s the usual choices if you don&rsquo;t want to go down to C/C++. Haskell has terrific performance, but its learning curve is less than stellar.</p>
<p>Erlang is a whole system, it has its own scheduling system, it controls living, breathing processes that each have its own garbage collector, it controls system signal trapping and so on. It was designed to be a full server. It is much smaller than a full blown Java Enterprise Edition container, so small that you can actually package command line tools that do start up fast enough. But this is not the sweet spot. For that purpose you will be better off with Go or even Rust.</p>
<p>For the same reasons it was not made to be an embeddable language the way Lua is. It was not made to create libraries that can be easily linked to via FFI or C-like function exports, the way Rust can.</p>
<p>There are ways to create desktop-class applications, specially with cross-platform <a href="http://www.erlang.org/doc/apps/wx/chapter.html"target="_blank" rel="noopener">wxWidgets</a>, the way the built-in Observer instrumentation application is done, but Erlang was not built to be a desktop toolkit.</p>
<p>Also because it prioritizes correctness, rigidly isolated processes communicating just by opaque messages, immutable and non-shared states, it means that Erlang is not suited to hard data science processing. So I doubt it&rsquo;s the best choice for Big Data analytics, DNA sequencing and other hard stuff that tools like Julia, R, Fortran, are better choices. It&rsquo;s not the same thing to say that it can&rsquo;t be a good database core, Riak and CouchDB proved that already. But complex queries on top of high volumes of data is not the sweet spot as well.</p>
<p>So, Erlang is good for distributed systems, with high concurrency of opaque message exchange and proxying. The exact scenario where the Web is. Web Applications heavy load of throughput that need real time chats and notifications, heavy and time consuming payment transactions, gathering of data from many sources in order to reduce them to consumable HTML or JSON responses.</p>
<p>But for the average Web developer (and by &ldquo;average&rdquo; I mean minimally able to architect the kinds of complex systems we deal with everyday in web development, not simple static website construction), Erlang used to be a real challenge, and now we can have the comfort of a real modern language with hints of Ruby and Clojure, without the complexities of strong typing but with the security of its built-in Fault Tolerance constructions in order to deliver highly reliable, highly scalable, modern Web applications.</p>
]]></content:encoded><category>elixir-and-erlang</category><category>distributed-systems</category><category>software-architecture</category></item><item><title>"Yocto Services"! And My First Month with Elixir!</title><link>https://akitaonrails.github.io/en/2015/11/25/yocto-services-and-my-first-month-with-elixir/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/11/25/yocto-services-and-my-first-month-with-elixir/</guid><pubDate>Wed, 25 Nov 2015 18:04:00 -0200</pubDate><description>&lt;p&gt;Wow, for the past month (almost, from Oct 27 to Nov 25) I decided that it was past time to dive deep and actually learn Elixir. I did just that, I am still a beginner but I feel very confident that I can tackle Elixir based projects now.&lt;/p&gt;
&lt;p&gt;For my learning process:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I read the entire &lt;a href="http://elixir-lang.org/getting-started/introduction.html"target="_blank" rel="noopener"&gt;official Elixir Documentation&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;I read the entire Dave Thomas&amp;rsquo; &lt;a href="https://pragprog.com/book/elixir/programming-elixir"target="_blank" rel="noopener"&gt;Programming Elixir&lt;/a&gt; book;&lt;/li&gt;
&lt;li&gt;I watched almost all &lt;a href="http://elixirsips.com/"target="_blank" rel="noopener"&gt;Elixir Sips&lt;/a&gt; screencasts;&lt;/li&gt;
&lt;li&gt;I read the entire Benjamin Tan Wei Hao&amp;rsquo;s &lt;a href="https://www.manning.com/books/the-little-elixir-and-otp-guidebook"target="_blank" rel="noopener"&gt;The Little Elixir &amp;amp; OTP Guidebook&lt;/a&gt; book;&lt;/li&gt;
&lt;li&gt;I did a number of tutorials and exercises.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And finally, I documented everything I learned almost every day in the following articles:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Wow, for the past month (almost, from Oct 27 to Nov 25) I decided that it was past time to dive deep and actually learn Elixir. I did just that, I am still a beginner but I feel very confident that I can tackle Elixir based projects now.</p>
<p>For my learning process:</p>
<ol>
<li>I read the entire <a href="http://elixir-lang.org/getting-started/introduction.html"target="_blank" rel="noopener">official Elixir Documentation</a>;</li>
<li>I read the entire Dave Thomas&rsquo; <a href="https://pragprog.com/book/elixir/programming-elixir"target="_blank" rel="noopener">Programming Elixir</a> book;</li>
<li>I watched almost all <a href="http://elixirsips.com/"target="_blank" rel="noopener">Elixir Sips</a> screencasts;</li>
<li>I read the entire Benjamin Tan Wei Hao&rsquo;s <a href="https://www.manning.com/books/the-little-elixir-and-otp-guidebook"target="_blank" rel="noopener">The Little Elixir &amp; OTP Guidebook</a> book;</li>
<li>I did a number of tutorials and exercises.</li>
</ol>
<p>And finally, I documented everything I learned almost every day in the following articles:</p>
<ul>
<li><a href="http://www.akitaonrails.com/2015/10/27/how-fast-is-elixir-phoenix"target="_blank" rel="noopener">How Fast is Elixir/Phoenix?</a></li>
<li><a href="http://www.akitaonrails.com/2015/10/28/personal-thoughts-on-the-current-functional-programming-bandwagon"target="_blank" rel="noopener">Personal Thoughts on the Current Functional Programming Bandwagon</a></li>
<li><a href="http://www.akitaonrails.com/2015/10/29/phoenix-experiment-holding-2-million-websocket-clients"target="_blank" rel="noopener">Phoenix Experiment: Holding 2 Million Websocket clients!</a></li>
<li><a href="http://www.akitaonrails.com/en/2015/11/03/minha-primeira-semana-aprendendo-elixir"target="_blank" rel="noopener">My first week learning Elixir</a></li>
<li><a href="http://www.akitaonrails.com/en/2015/11/18/ex-manga-downloadr-um-exercicio-com-elixir"target="_blank" rel="noopener">Ex Manga Downloader, an exercise with Elixir</a></li>
<li><a href="http://www.akitaonrails.com/en/2015/11/19/ex-manga-downloadr-parte-2-poolboy-ao-resgate"target="_blank" rel="noopener">Ex Manga Downloadr - Part 2: Poolboy to the rescue!</a></li>
<li><a href="http://www.akitaonrails.com/en/2015/11/20/phoenix-15-minute-blog-comparacao-com-ruby-on-rails"target="_blank" rel="noopener">Phoenix &ldquo;15 Minute Blog&rdquo; comparison to Ruby on Rails</a></li>
<li><a href="http://www.akitaonrails.com/en/2015/11/22/observando-processos-em-elixir-the-little-elixir-otp-guidebook"target="_blank" rel="noopener">Observing Processes in Elixir - The Little Elixir &amp; OTP Guidebook</a></li>
<li><a href="http://www.akitaonrails.com/en/2015/11/25/exercicio-exmessenger-entendendo-nodes-em-elixir"target="_blank" rel="noopener">ExMessenger Exercise: Understanding Nodes in Elixir</a></li>
<li><a href="http://www.akitaonrails.com/en/2015/11/25/elixir-101-apresentando-a-sintaxe"target="_blank" rel="noopener">Elixir 101 - Introducing the Syntax</a></li>
</ul>
<p>Yes, I am prolific, and highly focused. I did the learning and writing all within less than a month (it would&rsquo;ve been less if I could have used night time and weekends). So I&rsquo;d say that the average developer would take at least 3 to 4 months to cover the same material.</p>
<h3>Initial Thinking: Yocto Services!<span class="hx:absolute hx:-mt-20" id="initial-thinking-yocto-services"></span>
    <a href="#initial-thinking-yocto-services" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>For good or for worse, we are in the dawn of the Micro Services architecture. In summary it&rsquo;s fragmenting your monolith into smaller applications that respond to HTTP based endpoints - that people call &ldquo;APIs&rdquo; - and that are responsible for a very narrow set of responsibilities. Then you create a &ldquo;front-end&rdquo; web application that will consume those services, such as analytics, payment methods, user authentication directories, and so on.</p>
<p>This is no novelty, of course, having HTTP APIs that return JSON structures is just a fancier way of doing the same, good, old <strong>Remote Procedure Calls</strong>, or RPCs, a technology we have had for decades to interconnect clients and servers in a network. But I digress.</p>
<p>If this is what people call &ldquo;Micro&rdquo; services, I think of Elixir processes as <strong>&ldquo;Yocto&rdquo;</strong> Services! (Milli &gt; Micro &gt; Nano &gt; Pico &gt; Femto &gt; Atto &gt; Zepto &gt; Yocto, by the way - I may have just invented a new term here!)</p>
<p>I described a bit of the Processes infrastructure, how to spawn them, how to exchange messages between them, and how to link them together. So, go read <a href="http://www.akitaonrails.com/en/2015/11/22/observando-processos-em-elixir-the-little-elixir-otp-guidebook"target="_blank" rel="noopener">my previous post</a> if you haven&rsquo;t already.</p>
<p>Inside an Elixir app you will find many processes, some from the VM running the show and some from your own app. If you architected it correctly, you&rsquo;ve implemented an OTP application, with proper groups of Supervisors and Children, all organized in a Supervision Tree. A small worker dies, its supervisor knows how to deal with it.</p>
<p>Now, here&rsquo;s the mental model: think of an Elixir process as a tiny micro-service - a <strong>Yocto service</strong>, if you will! - inside your application. In a Phoenix application, for example, you don&rsquo;t import a &ldquo;database library&rdquo; you actually spin up a &ldquo;Database Service&rdquo; (which is an Ecto Repo) that runs in parallel with the Endpoint application that responds to HTTP requests coming from the internet. The code in your controllers and models &ldquo;consume&rdquo; and send messages to the Ecto Repo &ldquo;Service&rdquo;. This is how you can visualize what&rsquo;s going on.</p>
<p>I have shown the Observer in the previous article as well. You will find a large tree if you open it up inside the IEx shell of a Phoenix application:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/531/big_phoenix-observer.png" alt="Observer Phoenix"  loading="lazy" /></p>
<p>In summary you will find the following section in the Pxblog Phoenix App Supervision Tree:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Pxblog.Supervisor
- Pxblog.Endpoint
    &#43; Pxblog.Endpoint.Server
    &#43; Pxblog.PubSub.Supervisor
        * Pxblog.PubSub
        * Pxblog.PubSub.Local
- Pxblog.Repo
    &#43; Pxblog.Repo.Pool</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is the Pxblog I explained in the <a href="http://www.akitaonrails.com/en/2015/11/20/phoenix-15-minute-blog-comparacao-com-ruby-on-rails"target="_blank" rel="noopener">Phoenix and Rails comparison article</a> I published a few days ago.</p>
<p>I still haven&rsquo;t read the source code of Phoenix, but if I am interpreting the Observer correctly, the Endpoint.Server controls a pool of processes that are TCP listeners that the application is ready to accept requests from, concurrently, with overflowing to accept more connections (I believe it&rsquo;s a pool implementation like Poolboy, which I explained in <a href="http://www.akitaonrails.com/en/2015/11/19/ex-manga-downloadr-parte-2-poolboy-ao-resgate"target="_blank" rel="noopener">Part 2 of the Ex Manga Downloader article</a>).</p>
<p>Then, you have the PubSub.Supervisor and PubSub.Local applications that I believe support the WebSocket channels.</p>
<p>The Repo alone controls 10 initial processes in its pool, possibly a database connection pool. Notice how Endpoint and Repo groups are in parallel branches in the supervision tree. If Repo fails for some external database problems, the Endpoint group does not have to fail. This is what&rsquo;s declared in the Pxblog Application definition at <tt>lib/pxblog.ex</tt>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>defmodule Pxblog do
  use Application

  # See http://elixir-lang.org/docs/stable/elixir/Application.html
  # for more information on OTP Applications
  def start(_type, _args) do
    import Supervisor.Spec, warn: false

    children = [
      # Start the endpoint when the application starts
      supervisor(Pxblog.Endpoint, []),
      # Start the Ecto repository
      worker(Pxblog.Repo, []),
      # Here you could define other workers and supervisors as children
      # worker(Pxblog.Worker, [arg1, arg2, arg3]),
    ]

    # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: Pxblog.Supervisor]
    Supervisor.start_link(children, opts)
  end
  ...
end</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>See how it defines Endpoint and Repo under the Pxblog.Supervisor.</p>
<p>I can go on and forcefully kill the entire Pxblog.Repo node from the Supervision Tree using the Observer, like I did in the previous article, and the proper strategy kicks in, the Phoenix Supervisor successfully restarts the Repo, and no one will ever notice something crashed underneath.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/532/big_phoenix_kill_repo.png" alt="Kill Repo"  loading="lazy" /></p>
<p>From the IEx I can still make more calls to the Repo like this and it responds as if it had never crashed:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(4)&gt; Pxblog.Repo.all(Pxblog.User)
[debug] SELECT u0.&#34;id&#34;, u0.&#34;username&#34;, u0.&#34;email&#34;, u0.&#34;password_digest&#34;, u0.&#34;inserted_at&#34;, u0.&#34;updated_at&#34; FROM &#34;users&#34; AS u0 [] OK query=78.2ms queue=3.2ms
[%Pxblog.User{__meta__: #Ecto.Schema.Metadata&lt;:loaded&gt;,
  email: &#34;akitaonrails@me.com&#34;, id: 1,
  inserted_at: #Ecto.DateTime&lt;2015-11-20T14:01:09Z&gt;, password: nil,
  password_confirmation: nil,
  password_digest: &#34;...&#34;,
  posts: #Ecto.Association.NotLoaded&lt;association :posts is not loaded&gt;,
  updated_at: #Ecto.DateTime&lt;2015-11-20T14:01:09Z&gt;, username: &#34;akitaonrails&#34;}]</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And the way I think about this is: my IEx shell is sending a message to the Yocto Service called Pxblog.Repo (in reality it&rsquo;s forwarding messages to the database adapter that then checks out a process from the pool). Just like I would consume external Micro Services through HTTP APIs.</p>
<p>So the landscape of your application is comprised of a series of processes and groups of supervised processes all working to compose a larger structure. As I said in previous articles, should one group of process collapse, its Supervisor kicks in, traps the error and uses its strategy to, for example, restart all of its children processes, bringing the app to a consistent state once more, and without you having to restart the entire Elixir application.</p>
<p>So each process can be a full blown Yocto Service, running online and waiting for other services to consume it, such as the Repo&rsquo;s workers.</p>
<h3>Divide and Conquer<span class="hx:absolute hx:-mt-20" id="divide-and-conquer"></span>
    <a href="#divide-and-conquer" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Again, as a disclaimer, I am still new to Elixir but the way I find easier to understand it, is like this:</p>
<ul>
<li>
<p>If you must deal with external resources, be it a File, or a Network Connection, or anything outside of the Erlang VM, you will want it to be a GenServer.</p>
</li>
<li>
<p>Then, if you have a GenServer, you want to start it up under a Supervisor (usually, the simple boilerplate that defines the children and restart strategy).</p>
</li>
<li>
<p>The number of GenServer processes you want to start up depends on how many parallel processes you want to have running. For example, if it is a database service, there is no point in starting up more than the available number of maximum connections your database allows. If you have several files to process, you want at most the number of available files or - in practice - just a few processes to deal with batches of files. You will usually want a pool of GenServer processes, and in this case you want to use Poolboy.</p>
</li>
<li>
<p>One GenServer may call other GenServers. You don&rsquo;t want to use a try/catch exception handling mechanism because you just need the particular GenServer process to crash if something goes wrong: if the file is corrupted or doesn&rsquo;t exist or if the network goes unstable or disconnects. The Supervisor will replace that process with a new GenServer process in its place and refill the Pool if needed.</p>
</li>
<li>
<p>You can make GenServers <a href="http://www.akitaonrails.com/en/2015/11/25/exercicio-exmessenger-entendendo-nodes-em-elixir"target="_blank" rel="noopener">talk remotely</a> using the Node feature I explained 2 posts ago, with the ExMessenger example. Then it would be like a normal Micro Services architecture, but where the inside Yocto Services are actually doing the talking.</p>
</li>
<li>
<p>Any transformation with no side-effects (transforming a simple input into a simple output), like getting an HTML body string and parsing it into a List of tuples, can be organized in a normal Module. Refer to libraries like <a href="https://github.com/philss/floki"target="_blank" rel="noopener">Floki</a>, to see how they are organized.</p>
</li>
</ul>
<p>Each Erlang VM (called BEAM) is a single OS process, that manages a single real thread per CPU core available in your machine. Each real thread is managed by its own BEAM Scheduler, which will slice processing time between the lightweight processes inside.</p>
<p>Each BEAM process has its own mailbox to receive messages (more correctly called a <strong>run-queue</strong>). I/O operations such as file management will run asynchronously and not block the scheduler, which will manage to run other processes while waiting for I/O.</p>
<p>Each BEAM process also has its own separated heap and garbage collector (a generational, 2 stage copy collector). Because each process has very little state (variables in a function) each garbage collector stop is super short and runs fast.</p>
<p>So, each BEAM VM can be thought of as an entire application infrastructure, with many Yocto Services available for your application to call.</p>
<p>And as I said in the <a href="http://www.akitaonrails.com/en/2015/11/25/exercicio-exmessenger-entendendo-nodes-em-elixir"target="_blank" rel="noopener">previous article</a>, each BEAM VM can remotely call other BEAM VMs and exchange messages between them as if they were in the same VM. The semantics are almost the same and you have distributed computing the easier way.</p>
<p>Erlang implemented a fantastic set of primitives that quickly scale as large as you want or need, with the proper wirings to not let you down. And Elixir fixes the only problem most people have with Erlang: its <a href="http://www.akitaonrails.com/en/2015/11/25/elixir-101-apresentando-a-sintaxe"target="_blank" rel="noopener">old Prolog-inspired weird syntax</a>. Elixir is a thin and effective coat of modern language design, partly inspired by Ruby (although not a port by any means, it&rsquo;s still Erlang underneath).</p>
<p>I hope this series helped shed some light on why Elixir is the best choice among the new generation of high concurrency enabled languages and I also hope I made myself clear on why just high concurrency is not enough: you want high reliability as well. And in this aspect, the OTP architecture, built-in with Erlang, has no competition.</p>
]]></content:encoded><category>elixir-and-erlang</category><category>distributed-systems</category><category>software-architecture</category></item><item><title>ExMessenger Exercise: Understanding Nodes in Elixir</title><link>https://akitaonrails.github.io/en/2015/11/25/exmessenger-exercise-understanding-nodes-in-elixir/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/11/25/exmessenger-exercise-understanding-nodes-in-elixir/</guid><pubDate>Wed, 25 Nov 2015 13:19:00 -0200</pubDate><description>&lt;p&gt;I was working through this &lt;a href="http://drew.kerrigan.io/ditributed-elixir/"target="_blank" rel="noopener"&gt;old 2014 blog post&lt;/a&gt; by Drew Kerrigan where he builds a bare-bones, command-line-based chat application, with a client that sends messages and commands to a server.&lt;/p&gt;
&lt;p&gt;This is Elixir pre-1.0, and because it&amp;rsquo;s an exercise I refactored the original code and merged the server (ex_messenger) and client (ex_messenger_client) projects into an &lt;a href="http://elixir-lang.org/getting-started/mix-otp/dependencies-and-umbrella-apps.html"target="_blank" rel="noopener"&gt;Elixir Umbrella&lt;/a&gt; project and you can find my code on &lt;a href="https://github.com/akitaonrails/ex_messenger_exercise/blob/master/apps/ex_messenger_client/lib/ex_messenger_client.ex"target="_blank" rel="noopener"&gt;Github here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you have multiple applications that work together and share the same dependencies you can use the Umbrella convention to have them all in the same codebase. If you &lt;tt&gt;mix compile&lt;/tt&gt; from the umbrella root, it compiles all the apps (which are independent Elixir mix projects as well). It&amp;rsquo;s just a way to have related apps in the same place instead of in multiple different repositories.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I was working through this <a href="http://drew.kerrigan.io/ditributed-elixir/"target="_blank" rel="noopener">old 2014 blog post</a> by Drew Kerrigan where he builds a bare-bones, command-line-based chat application, with a client that sends messages and commands to a server.</p>
<p>This is Elixir pre-1.0, and because it&rsquo;s an exercise I refactored the original code and merged the server (ex_messenger) and client (ex_messenger_client) projects into an <a href="http://elixir-lang.org/getting-started/mix-otp/dependencies-and-umbrella-apps.html"target="_blank" rel="noopener">Elixir Umbrella</a> project and you can find my code on <a href="https://github.com/akitaonrails/ex_messenger_exercise/blob/master/apps/ex_messenger_client/lib/ex_messenger_client.ex"target="_blank" rel="noopener">Github here</a>.</p>
<p>If you have multiple applications that work together and share the same dependencies you can use the Umbrella convention to have them all in the same codebase. If you <tt>mix compile</tt> from the umbrella root, it compiles all the apps (which are independent Elixir mix projects as well). It&rsquo;s just a way to have related apps in the same place instead of in multiple different repositories.</p>
<p>The code shown here is in <a href="https://github.com/akitaonrails/ex_messenger_exercise"target="_blank" rel="noopener">my personal Github repository</a> if you want to clone it.</p>
<h3>Nodes 101<span class="hx:absolute hx:-mt-20" id="nodes-101"></span>
    <a href="#nodes-101" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Before we check out the exercise, there is one more concept I need to clear up. In the previous article I explained how you can start processes and exchange messages, and how you can use the OTP GenServer and Supervisor to create more robust and fault-tolerant processes.</p>
<p>But this is just the beginning of the story. You&rsquo;ve probably heard how Erlang is great for distributed computing as well. Each Erlang VM (or BEAM) is network-enabled.</p>
<p>Again, this is one more concept I am still just beginning to properly learn, and you will want to read Elixir&rsquo;s website documentation on <a href="http://elixir-lang.org/getting-started/mix-otp/distributed-tasks-and-configuration.html"target="_blank" rel="noopener">Distributed tasks and configuration</a>, that does an excellent job explaining how all this works.</p>
<p>But just to get started you can simply start 2 IEx sessions. From one terminal you can do:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex --sname fabio --cookie chat

Erlang/OTP 18 [erts-7.1] [source] [64-bit] [smp:4:4] [async-threads:10] [kernel-poll:false]

Interactive Elixir (1.1.1) - press Ctrl&#43;C to exit (type h() ENTER for help)
iex(fabio@Hal9000u)1&gt; </code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And from a different terminal you can do:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex --sname akita --cookie chat

Erlang/OTP 18 [erts-7.1] [source] [64-bit] [smp:4:4] [async-threads:10] [kernel-poll:false]

Interactive Elixir (1.1.1) - press Ctrl&#43;C to exit (type h() ENTER for help)
iex(akita@Hal9000u)1&gt; </code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Notice how the IEx shell shows different Node names for each instance: &ldquo;fabio@Hal9000u&rdquo; and &ldquo;akita@Hal9000u&rdquo;. It&rsquo;s the sname concatenated with your machine name. From one instance you can ping the other, for example:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(akita@Hal9000u)2&gt; Node.ping(:&#34;fabio@Hal9000u&#34;)
:pong</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If the name is correct and the other instance is indeed up, it responds to the ping with a <tt>:pong</tt>. This works only for nodes on the same machine, but what if I need to connect to an instance on a remote machine?</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(akita@Hal9000u)3&gt; Node.ping(:&#34;fabio@192.168.1.13&#34;)

11:02:46.152 [error] ** System NOT running to use fully qualified hostnames **
** Hostname 192.168.1.13 is illegal **</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The <tt>&ndash;sname</tt> option sets a name only reachable within the same subnet; for a fully qualified domain name you need to use <tt>&ndash;name</tt>, for example like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex  --name fabio@192.168.1.13 --cookie chat</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And for the other node:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex --name akita@192.168.1.13 --cookie chat</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And from this second terminal you can ping the other node the same way as before:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(akita@192.168.1.13)1&gt; Node.ping(:&#34;fabio@192.168.1.13&#34;)
:pong</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And you might be wondering, what is this &ldquo;<tt>&ndash;cookie</tt>&rdquo; thing? Just spin up a third terminal with another client name, but without the cookie, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex --name john@192.168.1.13</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And if you try to ping one of the first two nodes you won&rsquo;t get a <tt>:pong</tt> back:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(john@192.168.1.13)1&gt; Node.ping(:&#34;fabio@192.168.1.13&#34;)
:pang</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The cookie is just an atom to identify relationships between nodes. In a pool of several servers you can make sure you&rsquo;re not trying to connect different applications to each other. And as a result you get a <tt>:pang</tt>. Instead of an IP address you can use a fully qualified domain name.</p>
<p>And just by having the node &ldquo;akita@&rdquo; pinging &ldquo;fabio@&rdquo; we can see that they are aware of each other:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(fabio@192.168.1.13)2&gt; Node.list
[:&#34;akita@192.168.1.13&#34;]</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(akita@192.168.1.13)2&gt; Node.list
[:&#34;fabio@192.168.1.13&#34;]</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If one of the nodes crashes or quits, the Node list is automatically refreshed to reflect only nodes that are actually alive and responding.</p>
<p>You can check the official API Reference for the <a href="http://elixir-lang.org/docs/stable/elixir/Node.html#summary"target="_blank" rel="noopener">Node</a> for more information. But this should give you a hint for the next section.</p>
<h3>Creating a Chat Client<span class="hx:absolute hx:-mt-20" id="creating-a-chat-client"></span>
    <a href="#creating-a-chat-client" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Back to the exercise: the ExMessenger server has &ldquo;ExMessenger.Server&rdquo;, which is a GenServer, and the &ldquo;ExMessenger.Supervisor&rdquo; that starts it up. The ExMessenger.Server is globally registered as <tt>:message_server</tt>, started and supervised by the &ldquo;ExMessenger.Supervisor&rdquo;.</p>
<p>The &ldquo;ExMessengerClient&rdquo; starts up the unsupervised &ldquo;ExMessengerClient.MessageHandler&rdquo;, which is also a GenServer, and globally registered as <tt>:message_handler</tt>.</p>
<p>The Tree for both apps looks roughly like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>ExMessenger
- ExMessenger.Supervisor
    &#43; ExMessenger.Server
ExMessengerClient
- ExMessengerClient.MessageHandler</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>We start them separately, first the message server:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>cd apps/ex_messenger
iex --sname server --cookie chocolate-chip -S mix run</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Notice that for this example we are starting with a simple name &ldquo;server&rdquo;, for the local subnet, and a cookie. It will respond as &ldquo;server@Hal9000u&rdquo; (Hal9000u being my local machine&rsquo;s name).</p>
<p>Then, we can start the client app:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>cd apps/ex_messenger_client
server=server@Hal9000u nick=john elixir --sname client -S mix run</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Here we are setting 2 environment variables (that we can retrieve inside the app using <tt>System.get_env/1</tt>) and also setting a local node name &ldquo;client&rdquo;. You can spin up more client nodes using a different &ldquo;sname&rdquo; and a different &ldquo;nick&rdquo; from another terminal, as many as you want, all linking to the same &ldquo;server@Hal9000u&rdquo; message server.</p>
<p>I&rsquo;m starting up like this instead of using a command-line escript like I did in ExMangaDownloadr because I didn&rsquo;t find any way to set the <tt>&ndash;sname</tt> or <tt>&ndash;name</tt> the same way I can set <tt>&ndash;cookie</tt> using <tt>Node.set_cookie</tt>. If anyone knows how to set it up differently, let me know in the comments section down below.</p>
<p>Notice that I said &ldquo;linking&rdquo; and not &ldquo;connecting&rdquo;. From the &ldquo;ExMessengerClient&rdquo; we start like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMessengerClient</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Application</span>
</span></span><span class="line"><span class="cl">  <span class="k">alias</span> <span class="no">ExMessengerClient</span><span class="o">.</span><span class="n">CLI</span>
</span></span><span class="line"><span class="cl">  <span class="k">alias</span> <span class="no">ExMessengerClient</span><span class="o">.</span><span class="n">ServerProcotol</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start</span><span class="p">(</span><span class="n">_type</span><span class="p">,</span> <span class="n">_args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get_env</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="n">connect</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="n">start_message_handler</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="n">join_chatroom</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="no">CLI</span><span class="o">.</span><span class="n">input_loop</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The <tt>get_env</tt> private function is just a wrapper to handle the &ldquo;server&rdquo; and &ldquo;nick&rdquo; environment variables that we passed:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">get_env</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">server</span> <span class="o">=</span> <span class="no">System</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="s2">&#34;server&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">rstrip</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">to_atom</span>
</span></span><span class="line"><span class="cl">  <span class="n">nick</span> <span class="o">=</span> <span class="no">System</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="s2">&#34;nick&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">rstrip</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now, we try to connect to the remote server:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">connect</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;Connecting to </span><span class="si">#{</span><span class="n">server</span><span class="si">}</span><span class="s2"> from </span><span class="si">#{</span><span class="no">Node</span><span class="o">.</span><span class="n">self</span><span class="si">}</span><span class="s2"> ...&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="no">Node</span><span class="o">.</span><span class="n">set_cookie</span><span class="p">(</span><span class="no">Node</span><span class="o">.</span><span class="n">self</span><span class="p">,</span> <span class="ss">:&#34;chocolate-chip&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="no">Node</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">server</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="kp">true</span> <span class="o">-&gt;</span> <span class="ss">:ok</span>
</span></span><span class="line"><span class="cl">    <span class="n">reason</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;Could not connect to server, reason: </span><span class="si">#{</span><span class="n">reason</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="no">System</span><span class="o">.</span><span class="n">halt</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The important piece here is that we are setting the client instance&rsquo;s cookie with <tt>Node.set_cookie/1</tt> (notice that we didn&rsquo;t pass it in the command-line options like we did with the server instance). Without setting the cookie, the next line with <tt>Node.connect(server)</tt> would fail to connect, as I explained in the previous section.</p>
<p>Then, we start the &ldquo;ExMessengerClient.MessageHandler&rdquo; GenServer, linking with the Message Server instance:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">start_message_handler</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">ExMessengerClient</span><span class="o">.</span><span class="n">MessageHandler</span><span class="o">.</span><span class="n">start_link</span><span class="p">(</span><span class="n">server</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;Connected&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The Message Handler GenServer itself is very simple. It just sets the server as the state, handles incoming messages from the server and prints them out in the client&rsquo;s terminal:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMessengerClient</span><span class="o">.</span><span class="n">MessageHandler</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">GenServer</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start_link</span><span class="p">(</span><span class="n">server</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="ss">:gen_server</span><span class="o">.</span><span class="n">start_link</span><span class="p">({</span> <span class="ss">:local</span><span class="p">,</span> <span class="ss">:message_handler</span> <span class="p">},</span> <span class="n">__MODULE__</span><span class="p">,</span> <span class="n">server</span><span class="p">,</span> <span class="o">[]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">server</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="ss">:ok</span><span class="p">,</span> <span class="n">server</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_cast</span><span class="p">({</span> <span class="ss">:message</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span> <span class="p">},</span> <span class="n">server</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">message</span> <span class="o">=</span> <span class="n">message</span> <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">rstrip</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;</span><span class="se">\n</span><span class="si">#{</span><span class="n">server</span><span class="si">}</span><span class="s2">&gt; </span><span class="si">#{</span><span class="n">nick</span><span class="si">}</span><span class="s2">: </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">write</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="no">Node</span><span class="o">.</span><span class="n">self</span><span class="si">}</span><span class="s2">&gt; &#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:noreply</span><span class="p">,</span> <span class="n">server</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Going back to the main &ldquo;ExMessengerClient&rdquo; module, after starting the (unsupervised) GenServer that receives incoming messages, we proceed to join the pseudo-chatroom in the server:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">join_chatroom</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="no">ServerProcotol</span><span class="o">.</span><span class="n">connect</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">users</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;* Joined the chatroom *&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;* Users in the room: </span><span class="si">#{</span><span class="n">users</span><span class="si">}</span><span class="s2"> *&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;* Type /help for options *&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">reason</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;Could not join chatroom, reason: </span><span class="si">#{</span><span class="n">reason</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="no">System</span><span class="o">.</span><span class="n">halt</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I defined this &ldquo;ServerProcotol&rdquo; module, which is just a convenience wrapper for <tt>GenServer.call/3</tt> and <tt>GenServer.cast/2</tt> calls, to send messages to the remote GenServer called <tt>:message_server</tt>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMessengerClient</span><span class="o">.</span><span class="n">ServerProcotol</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">connect</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">server</span> <span class="o">|&gt;</span> <span class="n">call</span><span class="p">({</span><span class="ss">:connect</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">disconnect</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">server</span> <span class="o">|&gt;</span> <span class="n">call</span><span class="p">({</span><span class="ss">:disconnect</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">list_users</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">server</span> <span class="o">|&gt;</span> <span class="n">cast</span><span class="p">({</span><span class="ss">:list_users</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">private_message</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">},</span> <span class="n">to</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">server</span> <span class="o">|&gt;</span> <span class="n">cast</span><span class="p">({</span><span class="ss">:private_message</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">to</span><span class="p">,</span> <span class="n">message</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">say</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">},</span> <span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">server</span> <span class="o">|&gt;</span> <span class="n">cast</span><span class="p">({</span><span class="ss">:say</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">call</span><span class="p">(</span><span class="n">server</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">GenServer</span><span class="o">.</span><span class="n">call</span><span class="p">({</span><span class="ss">:message_server</span><span class="p">,</span> <span class="n">server</span><span class="p">},</span> <span class="n">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">cast</span><span class="p">(</span><span class="n">server</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">GenServer</span><span class="o">.</span><span class="n">cast</span><span class="p">({</span><span class="ss">:message_server</span><span class="p">,</span> <span class="n">server</span><span class="p">},</span> <span class="n">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pretty straightforward. Then, the main ExMessengerClient calls the recursive <tt>input_loop/1</tt> function from the CLI module, which just receives user input and handles the proper commands using pattern matching, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMessengerClient</span><span class="o">.</span><span class="n">CLI</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">alias</span> <span class="no">ExMessengerClient</span><span class="o">.</span><span class="n">ServerProcotol</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">input_loop</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">write</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="no">Node</span><span class="o">.</span><span class="n">self</span><span class="si">}</span><span class="s2">&gt; &#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">line</span> <span class="o">=</span> <span class="no">IO</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="ss">:line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">rstrip</span>
</span></span><span class="line"><span class="cl">    <span class="n">handle_command</span> <span class="n">line</span><span class="p">,</span> <span class="p">{</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">input_loop</span> <span class="p">{</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="s2">&#34;/help&#34;</span><span class="p">,</span> <span class="n">_args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Available commands:
</span></span></span><span class="line"><span class="cl"><span class="s2">      /leave
</span></span></span><span class="line"><span class="cl"><span class="s2">      /join
</span></span></span><span class="line"><span class="cl"><span class="s2">      /users
</span></span></span><span class="line"><span class="cl"><span class="s2">      /pm &lt;to nick&gt; &lt;message&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">      or just type a message to send
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="s2">&#34;/leave&#34;</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">ServerProcotol</span><span class="o">.</span><span class="n">disconnect</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;You have exited the chatroom, you can rejoin with /join or quit with /quit&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="s2">&#34;/quit&#34;</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">ServerProcotol</span><span class="o">.</span><span class="n">disconnect</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">System</span><span class="o">.</span><span class="n">halt</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="s2">&#34;/join&#34;</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">ServerProcotol</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;Joined the chatroom&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="s2">&#34;/users&#34;</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">ServerProcotol</span><span class="o">.</span><span class="n">list_users</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="n">_args</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="ss">:ok</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="kp">nil</span><span class="p">,</span> <span class="n">_args</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="ss">:ok</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">String</span><span class="o">.</span><span class="n">contains?</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="s2">&#34;/pm&#34;</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="n">to</span><span class="p">,</span> <span class="n">message</span><span class="p">}</span> <span class="o">=</span> <span class="n">parse_private_recipient</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="no">ServerProcotol</span><span class="o">.</span><span class="n">private_message</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">to</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="no">ServerProcotol</span><span class="o">.</span><span class="n">say</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">parse_private_recipient</span><span class="p">(</span><span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span><span class="n">to</span><span class="o">|</span><span class="n">message</span><span class="o">]</span> <span class="o">=</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">slice</span><span class="p">(</span><span class="mi">4</span><span class="o">..-</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">split</span>
</span></span><span class="line"><span class="cl">    <span class="n">message</span> <span class="o">=</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="no">List</span><span class="o">.</span><span class="n">foldl</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="n">fn</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">acc</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">acc</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">x</span><span class="si">}</span><span class="s2">&#34;</span> <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">lstrip</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="n">to</span><span class="p">,</span> <span class="n">message</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this wraps up the Client.</p>
<h3>Creating a Chat Server<span class="hx:absolute hx:-mt-20" id="creating-a-chat-server"></span>
    <a href="#creating-a-chat-server" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The Chat Client sends GenServer messages to a remote <tt>{:message_server, server}</tt>, and in the example, <tt>server</tt> is just the sname &ldquo;server@Hal9000u&rdquo; atom.</p>
<p>Now, we need this <tt>:message_server</tt> and this is the &ldquo;ExMessenger.Server&rdquo; GenServer:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMessenger</span><span class="o">.</span><span class="n">Server</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">GenServer</span>
</span></span><span class="line"><span class="cl">  <span class="nb">require</span> <span class="no">Logger</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start_link</span><span class="p">(</span><span class="o">[]</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="ss">:gen_server</span><span class="o">.</span><span class="n">start_link</span><span class="p">({</span> <span class="ss">:local</span><span class="p">,</span> <span class="ss">:message_server</span> <span class="p">},</span> <span class="n">__MODULE__</span><span class="p">,</span> <span class="o">[]</span><span class="p">,</span> <span class="o">[]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="o">[]</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="ss">:ok</span><span class="p">,</span> <span class="no">HashDict</span><span class="o">.</span><span class="n">new</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And that&rsquo;s it. When the &ldquo;ExMessenger.Supervisor&rdquo; starts this GenServer it registers it globally in this instance as <tt>:message_server</tt>. And this is how we address messages from what we called &ldquo;clients&rdquo; (the ExMessengerClient application).</p>
<p>When the ExMessengerClient calls the <tt>ServerProtocol.connect/1</tt>, it sends the <tt>{:connect, nick}</tt> message to the server. In the Server we handle it like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">handle_call</span><span class="p">({</span> <span class="ss">:connect</span><span class="p">,</span> <span class="n">nick</span> <span class="p">},</span> <span class="p">{</span><span class="n">from</span><span class="p">,</span> <span class="n">_</span><span class="p">}</span> <span class="p">,</span> <span class="n">users</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">cond</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">nick</span> <span class="o">==</span> <span class="ss">:server</span> <span class="ow">or</span> <span class="n">nick</span> <span class="o">==</span> <span class="s2">&#34;server&#34;</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:reply</span><span class="p">,</span> <span class="ss">:nick_not_allowed</span><span class="p">,</span> <span class="n">users</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="no">HashDict</span><span class="o">.</span><span class="n">has_key?</span><span class="p">(</span><span class="n">users</span><span class="p">,</span> <span class="n">nick</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:reply</span><span class="p">,</span> <span class="ss">:nick_in_use</span><span class="p">,</span> <span class="n">users</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kp">true</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="n">new_users</span> <span class="o">=</span> <span class="n">users</span> <span class="o">|&gt;</span> <span class="no">HashDict</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">nick</span><span class="p">,</span> <span class="n">node</span><span class="p">(</span><span class="n">from</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="n">user_list</span> <span class="o">=</span> <span class="n">log</span><span class="p">(</span><span class="n">new_users</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="s2">&#34;has joined&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:reply</span><span class="p">,</span> <span class="p">{</span> <span class="ss">:ok</span><span class="p">,</span> <span class="n">user_list</span> <span class="p">},</span> <span class="n">new_users</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>First, it checks if the nick is &ldquo;server&rdquo; and disallows it. Second, it checks if the nickname already exists in the internal <a href="http://elixir-lang.org/docs/stable/elixir/HashDict.html"target="_blank" rel="noopener">HashDict</a> (a key/value dictionary) and refuses if it already exists. Finally, third, it puts the pair of nickname and node name (like &ldquo;client@Hal9000u&rdquo;) in the HashDict and broadcasts through the <tt>log/3</tt> private function to all other nodes in the HashDict dictionary.</p>
<p>The <tt>log/3</tt> just creates a log message concatenating the nicknames of all clients and prints it out, then broadcasts it to the Message Handler of all the clients listed in the HashDict:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">log</span><span class="p">(</span><span class="n">users</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">user_list</span> <span class="o">=</span> <span class="n">users</span> <span class="o">|&gt;</span> <span class="no">HashDict</span><span class="o">.</span><span class="n">keys</span> <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34;:&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="no">Logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">&#34;</span><span class="si">#{</span><span class="n">nick</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2">, user_list: </span><span class="si">#{</span><span class="n">user_list</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">say</span><span class="p">(</span><span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">user_list</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">say</span><span class="p">(</span><span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">GenServer</span><span class="o">.</span><span class="n">cast</span><span class="p">(</span><span class="ss">:message_server</span><span class="p">,</span> <span class="p">{</span> <span class="ss">:say</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="s2">&#34;* </span><span class="si">#{</span><span class="n">nick</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2"> *&#34;</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">handle_cast</span><span class="p">({</span> <span class="ss">:say</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span> <span class="p">},</span> <span class="n">users</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">ears</span> <span class="o">=</span> <span class="no">HashDict</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">users</span><span class="p">,</span> <span class="n">nick</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="no">Logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">&#34;</span><span class="si">#{</span><span class="n">nick</span><span class="si">}</span><span class="s2"> said </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">broadcast</span><span class="p">(</span><span class="n">ears</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="ss">:noreply</span><span class="p">,</span> <span class="n">users</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Up to this point it just casts a message to itself, the <tt>{:say, nick, message}</tt> tuple, which is handled by the GenServer and calls the <tt>broadcast/3</tt> function defined like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">broadcast</span><span class="p">(</span><span class="n">users</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="n">users</span><span class="p">,</span> <span class="n">fn</span> <span class="p">{</span><span class="n">_</span><span class="p">,</span> <span class="n">node</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="no">Task</span><span class="o">.</span><span class="n">async</span><span class="p">(</span><span class="n">fn</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="n">send_message_to_client</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="no">Task</span><span class="o">.</span><span class="n">await</span><span class="o">/</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">send_message_to_client</span><span class="p">(</span><span class="n">client_node</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">GenServer</span><span class="o">.</span><span class="n">cast</span><span class="p">({</span> <span class="ss">:message_handler</span><span class="p">,</span> <span class="n">client_node</span> <span class="p">},</span> <span class="p">{</span> <span class="ss">:message</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It maps the list of users and fires up an asynchronous Elixir <tt>Task</tt> (which is itself just a GenServer, as I explained before in the Ex Manga Downloader series). Because it&rsquo;s a broadcast, it makes sense to make all of them parallel.</p>
<p>The important bit is the <tt>send_message_to_client/3</tt> which casts a message to the tuple <tt>{ :message_handler, client_node }</tt> where &ldquo;client_node&rdquo; is just &ldquo;client@Hal9000u&rdquo; or whatever &ldquo;&ndash;sname&rdquo; you used to start up each client node.</p>
<p>Now, this is how the clients send GenServer message calls/casts to <tt>{:message_server, server}</tt> and it sends messages back to <tt>{:message_handler, client}</tt>.</p>
<h3>This is not your traditional TCP Client/Server example!<span class="hx:absolute hx:-mt-20" id="this-is-not-your-traditional-tcp-clientserver-example"></span>
    <a href="#this-is-not-your-traditional-tcp-clientserver-example" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Now, we are calling the &ldquo;ExMessenger.Server&rdquo; a Chat &ldquo;Server&rdquo; and the &ldquo;ExMessengerClient&rdquo; a Chat &ldquo;Client&rdquo;. Although we have been calling them &ldquo;Server&rdquo; and &ldquo;Client&rdquo;, they don&rsquo;t refer to the usual &ldquo;TCP Server&rdquo; and &ldquo;TCP Client&rdquo; examples you may be familiar with!</p>
<p>The ExMessenger.Server is indeed a Server (an OTP GenServer) but the ExMessengerClient.MessageHandler is also a Server (another OTP GenServer)! Because they <strong>both</strong> have Node behavior, it&rsquo;s more like they are 2 peer-to-peer nodes instead of your old-school, simple client-server relationship. They can have client behavior (the Server sends messages to the MessageHandler) and server behavior (the Server receives messages from ExMessengerClient).</p>
<p>Let this concept sink in for a moment: built into the language you get a full-blown, easy-to-use, peer-to-peer network distribution model. You don&rsquo;t need to have one single node elected as the sole &ldquo;master&rdquo;; you could have all nodes in a ring coordinating between them, avoiding single points of failure.</p>
<p>I believe this is maybe how Erlang-based services such as <a href="http://manpages.ubuntu.com/manpages/hardy/man8/ejabberd.8.html"target="_blank" rel="noopener">ejabberd</a> and <a href="https://www.rabbitmq.com/clustering.html"target="_blank" rel="noopener">RabbitMQ</a> work.</p>
<p>In the <a href="https://github.com/processone/ejabberd/blob/master/src/ejabberd_cluster.erl"target="_blank" rel="noopener">case of ejabberd</a>, I can see that it keeps the state of the cluster in Mnesia tables (Mnesia being another component of OTP, a built-in distributed NoSQL database!) and it indeed uses the Node facilities to coordinate distributed nodes:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="nf">join</span><span class="p">(</span><span class="nv">Node</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="p">{</span><span class="nb">node</span><span class="p">(),</span> <span class="nn">net_adm</span><span class="p">:</span><span class="nf">ping</span><span class="p">(</span><span class="nv">Node</span><span class="p">)}</span> <span class="k">of</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="nv">Node</span><span class="p">,</span> <span class="p">_}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span><span class="n">error</span><span class="p">,</span> <span class="p">{</span><span class="n">not_master</span><span class="p">,</span> <span class="nv">Node</span><span class="p">}};</span>
</span></span><span class="line"><span class="cl">        <span class="p">{_,</span> <span class="n">pong</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nn">application</span><span class="p">:</span><span class="nf">stop</span><span class="p">(</span><span class="n">ejabberd</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nn">application</span><span class="p">:</span><span class="nf">stop</span><span class="p">(</span><span class="n">mnesia</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nn">mnesia</span><span class="p">:</span><span class="nf">delete_schema</span><span class="p">([</span><span class="nb">node</span><span class="p">()]),</span>
</span></span><span class="line"><span class="cl">            <span class="nn">application</span><span class="p">:</span><span class="nf">start</span><span class="p">(</span><span class="n">mnesia</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nn">mnesia</span><span class="p">:</span><span class="nf">change_config</span><span class="p">(</span><span class="n">extra_db_nodes</span><span class="p">,</span> <span class="p">[</span><span class="nv">Node</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">            <span class="nn">mnesia</span><span class="p">:</span><span class="nf">change_table_copy_type</span><span class="p">(</span><span class="n">schema</span><span class="p">,</span> <span class="nb">node</span><span class="p">(),</span> <span class="n">disc_copies</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nb">spawn</span><span class="p">(</span><span class="k">fun</span><span class="p">()</span>  <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nn">lists</span><span class="p">:</span><span class="nf">foreach</span><span class="p">(</span><span class="k">fun</span><span class="p">(</span><span class="nv">Table</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">                            <span class="nv">Type</span> <span class="o">=</span> <span class="n">call</span><span class="p">(</span><span class="nv">Node</span><span class="p">,</span> <span class="n">mnesia</span><span class="p">,</span> <span class="n">table_info</span><span class="p">,</span> <span class="p">[</span><span class="nv">Table</span><span class="p">,</span> <span class="n">storage_type</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">                            <span class="nn">mnesia</span><span class="p">:</span><span class="nf">add_table_copy</span><span class="p">(</span><span class="nv">Table</span><span class="p">,</span> <span class="nb">node</span><span class="p">(),</span> <span class="nv">Type</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="k">end</span><span class="p">,</span> <span class="nn">mnesia</span><span class="p">:</span><span class="nb">system_info</span><span class="p">(</span><span class="n">tables</span><span class="p">)</span><span class="o">--</span><span class="p">[</span><span class="n">schema</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">                <span class="k">end</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nn">application</span><span class="p">:</span><span class="nf">start</span><span class="p">(</span><span class="n">ejabberd</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">_</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span><span class="n">error</span><span class="p">,</span> <span class="p">{</span><span class="n">no_ping</span><span class="p">,</span> <span class="nv">Node</span><span class="p">}}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">.</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is what a snippet of pure Erlang source code looks like, by the way. You should have enough Elixir in your head right now to be able to abstract away the ugly Erlang syntax and see that it&rsquo;s a <tt>case</tt> pattern matching on the <tt>{_, :pong}</tt> tuple, using Node&rsquo;s ping facilities to assert the connectivity of the node and updating the Mnesia table and other setups.</p>
<p>Also in the <a href="https://github.com/rabbitmq/rabbitmq-server/blob/6f70dcbe05dbba35f7d950674d293a4c7d867d44/src/rabbit_control_main.erl"target="_blank" rel="noopener">source code of the RabbitMQ-Server</a> you will find a similar thing:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="nf">become</span><span class="p">(</span><span class="nv">BecomeNode</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">error_logger</span><span class="p">:</span><span class="nf">tty</span><span class="p">(</span><span class="n">false</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">ok</span> <span class="o">=</span> <span class="nn">net_kernel</span><span class="p">:</span><span class="nf">stop</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nn">net_adm</span><span class="p">:</span><span class="nf">ping</span><span class="p">(</span><span class="nv">BecomeNode</span><span class="p">)</span> <span class="k">of</span>
</span></span><span class="line"><span class="cl">        <span class="n">pong</span> <span class="o">-&gt;</span> <span class="nb">exit</span><span class="p">({</span><span class="n">node_running</span><span class="p">,</span> <span class="nv">BecomeNode</span><span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="n">pang</span> <span class="o">-&gt;</span> <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;  * Impersonating node: </span><span class="si">~s</span><span class="s">...&#34;</span><span class="p">,</span> <span class="p">[</span><span class="nv">BecomeNode</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span><span class="n">ok</span><span class="p">,</span> <span class="p">_}</span> <span class="o">=</span> <span class="nn">rabbit_cli</span><span class="p">:</span><span class="nf">start_distribution</span><span class="p">(</span><span class="nv">BecomeNode</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34; done</span><span class="si">~n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[]),</span>
</span></span><span class="line"><span class="cl">                <span class="nv">Dir</span> <span class="o">=</span> <span class="nn">mnesia</span><span class="p">:</span><span class="nb">system_info</span><span class="p">(</span><span class="n">directory</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;  * Mnesia directory  : </span><span class="si">~s~n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[</span><span class="nv">Dir</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">.</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Again, pinging nodes, using Mnesia for the server state. Erlang&rsquo;s syntax is uncommon for most of us: variables start with a capitalized letter (we intuitively think they&rsquo;re constants instead), statements end with a dot, instead of dot-notation to call a function from a module it uses a colon &ldquo;:&rdquo;, unlike Elixir the parentheses are not optional, and so on. Trying to read code like this shows the value of having Elixir to unleash Erlang&rsquo;s hidden powers.</p>
<p>So, up to this point, you know how processes are spawned internally, how they are orchestrated within the OTP framework, and now how they can interact remotely through the <strong>Node</strong> peer-to-peer abstraction. And again, this is all built into the language. No other language comes even close.</p>
]]></content:encoded><category>elixir-and-erlang</category><category>distributed-systems</category><category>software-architecture</category></item><item><title>[Off-Topic] The Myth of the "Legacy"</title><link>https://akitaonrails.github.io/en/2012/08/15/off-topic-o-mito-do-legado/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2012/08/15/off-topic-o-mito-do-legado/</guid><pubDate>Wed, 15 Aug 2012 12:28:00 -0300</pubDate><description>&lt;p&gt;If there&amp;rsquo;s a recurring story in any software development, it&amp;rsquo;s what I call the &amp;ldquo;Myth of the Legacy.&amp;rdquo; If you&amp;rsquo;re a programmer, you&amp;rsquo;ve been through this: you inherited code done by another person or another team, you see that it&amp;rsquo;s all very badly done, and your recommendation is to throw it all out and start over. You&amp;rsquo;re sure this is the only healthy alternative, and not doing it would be a huge mistake.&lt;/p&gt;</description><content:encoded><![CDATA[<p>If there&rsquo;s a recurring story in any software development, it&rsquo;s what I call the &ldquo;Myth of the Legacy.&rdquo; If you&rsquo;re a programmer, you&rsquo;ve been through this: you inherited code done by another person or another team, you see that it&rsquo;s all very badly done, and your recommendation is to throw it all out and start over. You&rsquo;re sure this is the only healthy alternative, and not doing it would be a huge mistake.</p>
<p>This behavior is what I also call &ldquo;Amateur Hysteria.&rdquo; And I say this with total peace of mind because I myself have had moments like this.</p>
<p>First of all, let&rsquo;s define what is commonly called &ldquo;Legacy&rdquo;: basically any code done up until yesterday, especially if you weren&rsquo;t the one who made it, is considered &ldquo;Legacy.&rdquo;</p>
<p>But what most programmers fail to understand is very simple: with that definition, any code will always automatically become &ldquo;legacy&rdquo; the minute after it&rsquo;s finished: including your own.</p>
<p>The neighbor&rsquo;s garden always looks greener? For programmers, the neighbor&rsquo;s code always looks worse. How many times have we heard?</p>
<ul>
<li><em>&ldquo;How could it take all this time to make this code? I would have done it in less time.&rdquo;</em></li>
<li><em>&ldquo;How could the code have been made this ugly way? I would have done it better.&rdquo;</em></li>
</ul>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/image_asset/image/19/BuildingLegacy.png" alt="Building the Legacy System of Tomorrow"  loading="lazy" /></p>
<h3>Context, context, context<span class="hx:absolute hx:-mt-20" id="context-context-context"></span>
    <a href="#context-context-context" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Why did a certain project take <em>&ldquo;more time than I think it should have?&rdquo;</em> Dozens of reasons: departments that didn&rsquo;t collaborate, poorly defined business rules that changed several times, and so on.</p>
<p>Why isn&rsquo;t the code as <em>&ldquo;elegant&rdquo;</em> or <em>&ldquo;well done&rdquo;</em> as <em>&ldquo;I would do it?&rdquo;</em> Because it was born with a goal in mind, and that goal changed, the way it always changes. The code piled up, the deadlines tightened, the refactorings didn&rsquo;t happen the way they should. Technical debt grew despite the warnings of the programmers involved, and the result ended up much worse than it should be.</p>
<p>Another thing that should be obvious: every programmer will always find something they don&rsquo;t like in any code, whether it&rsquo;s effectively bad and poorly made or even well-made and well-structured. Software is so complex you can find a flaw in anything. It&rsquo;s like pointing out flaws in a human being: everyone is imperfect.</p>
<p>The fallacy of whoever is listening is to think that just because someone pointed out a few flaws, <em>everything</em> is automatically bad. It&rsquo;s the reasoning that goes: <em>&ldquo;my new programmer said they found this flaw and this other poorly done thing, so the system is bad.&rdquo;</em> Except you didn&rsquo;t look at the qualities and didn&rsquo;t weigh whether the flaws actually outrank them in volume, criticality, and severity.</p>
<p>More than that: any new code will also have points another programmer won&rsquo;t like. And this cycle is infinite.</p>
<p>Now the truth: the programmer who arrived later and questioned all these points, had they been at the exact same moment when everything started, would have delivered the same bad code as the final result, or even worse.</p>
<h3>Fear<span class="hx:absolute hx:-mt-20" id="fear"></span>
    <a href="#fear" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Now the challenge is to understand the following. In most cases, &ldquo;Legacy&rdquo; code is code that is in production, in use, generating results. Something your &ldquo;new and elegant&rdquo; code isn&rsquo;t doing yet.</p>
<p>Another story that many programmers prefer to ignore is the <em>&ldquo;Big Rewrite&rdquo;</em> that was never concluded and never went live. Especially when you were the one who worked on it.</p>
<p>The worst type of software is that which doesn&rsquo;t generate value. And a factor that differentiates a professional programmer from an amateur is exactly how they deal with others&rsquo; code. Talking yesterday with <a href="http://blog.aspercom.com.br"target="_blank" rel="noopener">Rodrigo Yoshima</a>, he dropped a pearl: dealing with legacy is for adults, and any child can do &ldquo;greenfield&rdquo; (a project done from scratch).</p>
<p>It takes much more technique, much more skill, much more capability, to take a legacy and make it better, even if in some cases it is actually necessary to redo some parts. But that shouldn&rsquo;t be the first automatic alternative without justification. <em>&ldquo;Because I would do it better&rdquo;</em> isn&rsquo;t justification.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/image_asset/image/18/2006-12-08.gif" alt="Dilbert"  loading="lazy" /></p>
<h3>The Environment<span class="hx:absolute hx:-mt-20" id="the-environment"></span>
    <a href="#the-environment" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The process that facilitates teams generating bad code is always one of the hardest problems to solve. Bosses, clients who ask for <em>&ldquo;urgent last-minute changes&rdquo;</em> is one of the symptoms. Departments with different priorities. In environments like this, any new software will quickly be made worse to the point where it seems necessary to redo everything again.</p>
<p>I won&rsquo;t repeat everything the Agile communities have already said to this day, but the &ldquo;fix&rdquo; most of the time doesn&rsquo;t just involve programming techniques, but rather programmers who can see beyond the code and understand how to help fix the environment around them. This includes the programmer colleagues around, the bosses above, the demands coming from surrounding departments, and all the feedback coming from the market in the outside world.</p>
<p>In the real world, software is made by people for people. Understanding that people aren&rsquo;t difficult means the challenge for a professional is much greater than following good practices or being specialized in this or that technique. A good professional knows that just complaining and sulking doesn&rsquo;t lead anywhere.</p>
<h3>Opportunities<span class="hx:absolute hx:-mt-20" id="opportunities"></span>
    <a href="#opportunities" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>As always, stories aren&rsquo;t rules, but they serve to illustrate a point. Years ago I was allocated alone to a big client who had a small &ldquo;legacy&rdquo; system that was extremely poorly made. A one-off allocation. And I can say &ldquo;poorly made&rdquo; because it effectively didn&rsquo;t execute as it should. It was technically flawed and returned many errors. It was exactly the kind of code that would make any programmer say, immediately, <em>&ldquo;throw it out and do it again!&rdquo;</em></p>
<p>I had 2 weeks to do something. Others had already tried, the client was already convinced to make a new one. No one would think it bad if I said nothing could be done besides starting from scratch. It was an attempt. And I hate saying I can&rsquo;t solve a problem.</p>
<p>They needed the current one to work minimally. I was a Java programmer at the time, and the system was one of the worst ASPs I had ever seen up to that point, mixed with several DCOM components that miraculously executed somehow.</p>
<p>In 2 weeks I understood what the business rule they wanted was. I understood why the code didn&rsquo;t work according to these rules. I understood where the data feeding it came from, how it was handled and stored, what results were expected, and how I should intervene to implement that processing.</p>
<p>I used exactly the same code, did a minimal sanity cleanup, removed what was unnecessary, and inserted the minimum necessary to reach the goals expected by the client within the deadline that was stipulated for me.</p>
<p>During those 2 weeks I understood more about the process, understood more about the client, understood more about the environment. Besides fixing the software I managed to build a good relationship. This good relationship, in a few months, led me to return to work for this client but this time on a 1-and-a-half-year project with a team of almost 10 people, where I could coordinate the development my way.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/image_asset/image/20/big_url.jpeg" alt="Save our Code"  loading="lazy" /></p>
<p>In this case, it was clear that the code was done in bad faith by a programmer who didn&rsquo;t really know what they were doing. Many pieces of software are really done by simply bad programmers. But I understand that many cases are also good programmers who had to solve a problem within a series of restrictions they had to deal with.</p>
<p>To me, &ldquo;legacy&rdquo; is software done precisely by those who call others&rsquo; software &ldquo;legacy.&rdquo; For me, &ldquo;legacy&rdquo; can often be an opportunity, precisely because few can handle it. And for me, people who judge others&rsquo; code without knowledge of the whole context are just another amateur wanting to show off.</p>
<p>Of course, this isn&rsquo;t a defense that all legacy software is justified and good. Not at all. My point here is focused on the automatic behavior of programmers who use this as an excuse.</p>
]]></content:encoded><category>software-engineering</category><category>software-architecture</category><category>business</category><category>off-topic</category></item><item><title>Chatting with Chris Wanstrath (Err the Blog/Github)</title><link>https://akitaonrails.github.io/en/2008/04/21/chatting-with-chris-wanstrath-err-the-blog-github/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2008/04/21/chatting-with-chris-wanstrath-err-the-blog-github/</guid><pubDate>Mon, 21 Apr 2008 19:12:00 -0300</pubDate><description>&lt;p&gt;Chris is a very accessible and easy-going guy, and I just got him out of AIM and started the interview right away. For those of you who never heard of &lt;strong&gt;&amp;lsquo;Chris Wanstrath&amp;rsquo;&lt;/strong&gt; , he is also known for Err the Blog and recently as one of the guys behind the &lt;strong&gt;Github&lt;/strong&gt; phenomenon.&lt;/p&gt;
&lt;p&gt;He answered everything in colorful detail and we speak a lot about his open source projects, performance, scalability and, of course, lots of Git and Github stuff. Hopefully it will make people even more excited about how the Ruby/Rails community is moving things forward all the time.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Chris is a very accessible and easy-going guy, and I just got him out of AIM and started the interview right away. For those of you who never heard of <strong>&lsquo;Chris Wanstrath&rsquo;</strong> , he is also known for Err the Blog and recently as one of the guys behind the <strong>Github</strong> phenomenon.</p>
<p>He answered everything in colorful detail and we speak a lot about his open source projects, performance, scalability and, of course, lots of Git and Github stuff. Hopefully it will make people even more excited about how the Ruby/Rails community is moving things forward all the time.</p>
<p><strong>aos leitores brasileiros:</strong> assim que tiver tempo irei traduzir esta entrevista.</p>
<p><strong>AkitaOnRails:</strong> Ok, so, where are you from?</p>
<p><strong>Chris Wanstrath:</strong> Right now I live in San Francisco but I grew up in Cincinnati, Ohio. I moved to San Francisco in 2005 to work for C|NET where I did PHP for a while on sites like <a href="http://gamespot.com"target="_blank" rel="noopener">gamespot.com</a>, <a href="http://mp3.com"target="_blank" rel="noopener">mp3.com</a>, and <a href="http://tv.com"target="_blank" rel="noopener">tv.com</a></p>
<p>In 2006 I moved over to the <a href="http://chowhound.com"target="_blank" rel="noopener">chowhound.com</a> site within C|NET to work on Rails, which is something I had been doing on the side for a while. While I was there <strong>P.J. Hyett</strong> and myself worked on chowhound.com then built <a href="http://chow.com"target="_blank" rel="noopener">chow.com</a>, which we launched later that year. Then in April of 2007 we both left C|NET to form our consulting company, Err Free.</p>
<p>In December of 2007 we launched <a href="http://famspam.com/"target="_blank" rel="noopener">FamSpam</a>, which is a site for keeping in touch with your family. It&rsquo;s basically a simplified version of Google Groups with emphasis on participating in a thread at least once a week and easily finding photos or other file uploads.</p>
<p>While P.J. and I were working on FamSpam, <strong>Tom Preston-Werner</strong> and I were working on GitHub on the side. We started in October working nights and weekends on it. We basically wanted an easy, pretty, and feature-rich way to throw our Git repos up and share them with people. Then as we got more into it, we realized how things like the dashboard could change the way we deal with open source contributions. After that, all the social features began to fall into place. Github private beta launched in january, and in april we saw the public launch</p>
<p><strong>AkitaOnRails:</strong> Wow, Cool, you are a very busy guy indeed, I want to go on in details within each topic you mentioned but first I knew you worked for C|Net because of your memcached keynotes. I have 2 questions: you were a PHP programmer, what made you switch to Ruby on Rails? And then, working for a high profile network like C|Net exposed you to very demanding public websites. How did you figured out about leveraging memcached with Rails and then your cache_fu plugin?</p>
<p><strong>Chris Wanstrath:</strong> I started doing Rails at the start of 2005 and got my first PHP job in February of 2005. (before that I was doing asp and Perl, which I don&rsquo;t like to talk about). So really, I never considered myself a &ldquo;PHP programmer&rdquo; over a Rails programmer. I really liked websites and I knew I could get paid doing PHP, so that&rsquo;s what I did. In the meantime, I was constantly doing Ruby on the side.</p>
<p>When C|Net started doing Rails, I knew it would be a perfect fit for me. I really liked PHP and loved Gamespot, but it was just silly for me to spend all night playing with Ruby while doing PHP professionally.</p>
<p>In fact, one of the things that got me hired at Gamespot was my pure PHP yaml parser called <a href="http://spyc.sourceforge.net/"target="_blank" rel="noopener">SPYC</a> (simple PHP yaml class). It was inspired (obviously) by _why&rsquo;s syck yaml parser. I wanted an easy, extension-free way to deal with YAML in PHP because I was so spoiled by Ruby.</p>
<p>I think today the CakePHP and Symfony frameworks use it, and do their config stuff in yaml. But even when writing PHP I wanted to be writing Ruby. The main reason I picked up ruby in the first place is because I knew PHP, and I knew Perl, but I wanted something I could use to write both websites and command line scripts. Perl was great for scripts, and PHP was great for sites, but I was just tired of using them both</p>
<p>So while I was learning Python, Rails popped up.</p>
<p>As far as <a href="http://errtheblog.com/posts/57-kickin-ass-w-cachefu"target="_blank" rel="noopener">cache_fu</a> goes, I was very interested in some of the &ldquo;scaling&rdquo; aspects of running a website while working at Gamespot.</p>
<p>While good OOP design and stuff like that can be learned by anyone writing enough code, you can&rsquo;t really learn about scaling unless you&rsquo;re working on a big website. There are just too many tricks and moving parts, and it&rsquo;s too hard to simulate the load in a controlled environment.</p>
<p>So while at Gamespot, like I said, I tried to learn as much as possible about things like MySQL replication, clustering databases, sharding databases, and memcached based on how the smart guys at C|Net had set it all up over the years. They had a lot of machines serving a lot of traffic so it was perfect</p>
<p>At Chowhound, we were re-writing an old static html site in Rails and were worried about the transition from flat files to dynamic database calls and page generation. It had a fair amount of traffic from a group of rabid fans, and we knew the new &ldquo;web 2.0&rdquo; look of the site would not sit well with the old school users, so we at least wanted the site to be performant in order to give them one less thing to complain about. So we basically took a lot of what I had learned at Gamespot, and what information was freely available on the internet in mailing lists and discussions, and tried to do a more Rails friendly way of object caching with memcached.</p>
<p>Ruby is just so good at cleaning up repetition and creating friendly apis. So cache_fu does fragment caching and object caching, because in bigger sites you end up using both for different purposes which is how it was at Gamespot too, basically.</p>
<p><strong>AkitaOnRails:</strong> Nice. And it is not very common for me to find people that have dealt with really big websites. Can you give us a glimpse of what kind of load your Rails apps did have to handle? Some pundits I know complain about the vanilla Rails distribution and recommend lots of tweaks like dumping ActiveRecord altogether and several other hacks. Do you have to hack Rails a lot in order to achieve the scalability you need or do you think most of it is good enough so you can avoid too much tweaking?</p>
<p><strong>Chris Wanstrath:</strong> I wouldn&rsquo;t really consider Chowhound to be a big Rails site anymore. Now that you&rsquo;ve got scribd and twitter and friends for sale and Yellowpages, Chowhound doesn&rsquo;t really make the list.</p>
<p>We ended up doing a lot of premature optimization on that site, and not enough real world optimization. I learned a lot of lessons there that I&rsquo;ve applied to other sites along the way. However, I recently had the chance to work with the friends for sale guys on their Facebook app which gets millions of page views a day and handles an insane amount of db records.</p>
<p>To my knowledge, the only AR hack we did was apply Pratik Naik&rsquo;s partial updates patch. This was because, with so many rows and such large indexes, you want to avoid modifying indexes when you don&rsquo;t need to, so saving only the changed columns/attributes can have a real big performance gain. Luckily, partial updates are now in Rails Core.</p>
<p>In my experience, you spend a lot more time trying to scale SQL before you even need to address AR. And once you get to the AR part, it&rsquo;s usually your application code. Fetching and saving too many records in before/after_save callbacks, full row writes like I was just describing, and generally not being a good SQL citizen. I would only ditch AR completely if I felt like I had no control over the SQL it generated. Which, as anyone knows, is not the case – you have a lot of control over it.</p>
<p>There are times, however, when AR is overkill and you really do need speed. On Github, we need to authenticate you every time you try and pull or push a repository over ssh. Loading an entire Rails environment, or even activerecord, was noticeably slow, so we switched to using sequel in a simple ruby script. Because we don&rsquo;t need AR&rsquo;s massive featureset and nice OOP capabilities for such a simple single row find, this made a lot of sense for us.</p>
<p><strong>AkitaOnRails:</strong> I agree with that, that&rsquo;s what I experienced too with regards to AR. So, and you&rsquo;re also the author of several other famous Rails plugins like will_paginate, Ambition, the original Sexy Migrations. Can you comment briefly about each. Am I forgetting any other plugins? Some of what you did became the &lsquo;de facto&rsquo; standards for Rails development, some people here use these plugins without really knowing you made them all.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/4/21/113964679_23c106ba1b.jpg" alt=""  loading="lazy" /><br>
<em>P.J Hyett at SXSW'06</em></p>
<p><strong>Chris Wanstrath:</strong> <a href="http://errtheblog.com"target="_blank" rel="noopener">Err the blog</a> is actually two people, P.J. Hyett and myself. I did not write <a href="http://errtheblog.com/posts/56-im-paginating-again"target="_blank" rel="noopener">will_paginate</a> – P.J. wrote the original version then Mislav Marohnić re-wrote it. Mislav is now the maintainer. But I still get all the credit, which is fine by me.</p>
<p>As far as the other plugins go, there&rsquo;s mofo, ambition, sexy migrations, cache_fu, acts_as_textiled, fixture_scenarios_builder, and gibberish.</p>
<p><a href="http://errtheblog.com/posts/35-me-and-uformats"target="_blank" rel="noopener">Mofo</a> is a microformat parser based on Hpricot. It&rsquo;s up on Github and unfortunately doesn&rsquo;t get enough love from me these days, but as far as I know it&rsquo;s still the gold standard for microformat parsing in Ruby.</p>
<p>It was the first gem that I released which doubled as a Rails plugin, which is how I think most plugins should be distributed in the future. Gems already have a solid distribution system and versioning scheme, and if Github becomes a gem source then there&rsquo;s almost no reason not to release plugins as gems</p>
<p><a href="http://errtheblog.com/posts/51-sexy-migrations"target="_blank" rel="noopener">Sexy migrations</a> were inspired by Hobo. Unfortunately their version was hobo-specific, so I wrote a plugin-friendly version. DHH later wrote his own version and rolled it into core, making a grand total of 3 distinct sexy migration implementations.</p>
<p><a href="http://errtheblog.com/posts/55-ya-talkin-gibberish"target="_blank" rel="noopener">Gibberish</a> is a pretty fun localization plugin I wrote <em>pro bono</em> for a client, just because I felt like the APIs of all the existing localization plugins were so difficult to work with. Hopefully, with the help of anyone interested, we can begin localizing Github in the near future.</p>
<p><a href="http://errtheblog.com/posts/64-even-more-ambitious"target="_blank" rel="noopener">Ambition</a> was inspired by dabbling in Erlang. I read a blog post explaining that <a href="http://www.infoq.com/news/2007/08/mnesia"target="_blank" rel="noopener">mnesia&rsquo;s</a> query syntax, which is all list comprehension (basically Ruby&rsquo;s version of the Enumerable module), was made possible by the ability to walk the parse tree and build an mnesia query that way.</p>
<p>So I started playing with the parse_tree gem to see if I could do the same for Ruby. It&rsquo;s now, of course, a full fledged framework for generating arbitrary queries for any RubyGem based on Enumerable. For simple queries, I really like treating tables as arrays, it feels very natural.</p>
<p><a href="http://errtheblog.com/posts/12-actsastextiled"target="_blank" rel="noopener">Acts_as_textiled</a> was the first Rails plugin I released. It&rsquo;s a testament to Rails that the plugin still works on edge today. The only other notable thing that Err started is the &ldquo;vendor everything&rdquo; term, which is now a part of Rails through <a href="http://ryandaigle.com/articles/2008/4/1/what-s-new-in-edge-rails-gem-dependencies"target="_blank" rel="noopener">config.gems</a>.</p>
<p>That, and the <a href="http://errtheblog.com/posts/21-cheat"target="_blank" rel="noopener">Cheat</a> gem (and site).</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/4/21/322610984_f5d9e82bda.jpg" alt=""  loading="lazy" /><br>
<em>Tom Preston-Werner</em></p>
<p><strong>AkitaOnRails:</strong> (Of course, pardon me P.J.!) And this raises another curiosity of mine: where did the name &ldquo;Err&rdquo; come from? It is both for <a href="http://errtheblog.com/"target="_blank" rel="noopener">Err the Blog</a> and <a href="http://errfree.com/"target="_blank" rel="noopener">Err Free</a> which is your company. And you mentioned P.J. and Tom. And you&rsquo;re all full time Railers, right? How did you team up with them and decide to start your own company? Many times people ask me about entrepreneurship and you&rsquo;re another good example.</p>
<p><strong>Chris Wanstrath:</strong> &ldquo;err&rdquo; is kind of a play on &ldquo;Typo,&rdquo; which was the popular Rails blogging engine when we started our blog, so it may be kind of confusing, but I actually am cofounder of two distinct companies.</p>
<p>There is <strong>Err Free</strong> and <strong>Logical Awesome</strong>. Err Free is Ruby &amp; Rails consulting and training. P.J. and I own the company, and we use it to do client work, speaking gigs, corporate training, that sort of thing. It also owns and operates FamSpam, which we use with our own families.</p>
<p>Logical Awesome, however, was founded by me and Tom. This is because I was developing FamSpam with P.J. and GitHub with Tom at the same time, separately. Today, however, P.J. is a part of Logical Awesome and is one of the GitHub developers.</p>
<p>Tom works at <strong>Powerset</strong> and doesn&rsquo;t actually do much Rails for GitHub, he mainly does flash, haxe, C, css, design, and ruby. And because he&rsquo;s the author of <a href="http://god.rubyforge.org/"target="_blank" rel="noopener">God</a>, I make him write all the config files, too. As far as deciding to start a company, I am someone that needs to be involved in whatever I&rsquo;m involved with on every level. At C|Net, there is only so much input you can have on a website that employs 30 people especially when there are dedicated product and &ldquo;vision&rdquo; people, whereas with Github, it&rsquo;s just me Tom and P.J.</p>
<p>If the site is slow, confusing, sucks, or doesn&rsquo;t improve your workflow, I can take responsibility for that. But if it&rsquo;s fast, simple, awesome, and life changing, I get to take responsibility for that, too. Which is very rewarding.</p>
<p>It also means, when you&rsquo;re cofounder of a mostly technical company, the discussions are a lot more logical. I saw decisions made at C|Net that directly contradicted a/b statistics we had gathered. At Logical Awesome, that would never happen. It is neither logical nor awesome. Basically: I wanted to find the ideal company so I found good people and started it.</p>
<p><strong>AkitaOnRails:</strong> Ok, so we come to the main dish of the day: <a href="http://github.com"target="_blank" rel="noopener">Github</a>! I can&rsquo;t think of a better example of an innovative product that leverages so well the power of Git. I am very curious to understand how did you come up with this idea? At the same time, lots of people still can&rsquo;t understand why Railers as a whole started using Git so massively all of a sudden. Mercurial, Bazaar guys are very uncomfortable. What do you think Git has that attracted Railers in flocks like that? Or at least what do you think Git has that no other have? I started evangelizing Git here in Brazil last year after listening Randall Schwartz, and recently someone asked me what would be my pick for a Rails killer-app and Github popped out of my mind, right away. So you do have good mind share as well.</p>
<p><a href="http://github.com"target="_blank" rel="noopener"><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/4/21/Picture_1.png" alt=""  loading="lazy" /></a></p>
<p><strong>Chris Wanstrath:</strong> Github is super exciting to me because it&rsquo;s really a website just for me. Tom and I are pretty similar in that we both have a fair amount of open source projects, all of which get used and patched by people we&rsquo;ve never met.</p>
<p>Handling this the old fashion way was fine until me and P.J. started working on FamSpam… as my free time decreased, so did my ability to maintain my open source projects. And really, that&rsquo;s a definite workflow problem. Sometimes when you can&rsquo;t find enough time to do something, you just need to make the something take less time.</p>
<p>Github was literally written for chronic, god, ambition, will_paginate, and the rest of our projects. We wanted to make it dead simple for people to contribute, and really, it&rsquo;s just not possible without Git.</p>
<p>I had started using Git right after viewing the <a href="http://www.youtube.com/watch?v=4XpnKHJAok8"target="_blank" rel="noopener">Linus tech talk</a> in May 2007, which is really good at explaining Git from a very high conceptual level. So when this time problem came up, I had already been using Git for a few months and Tom had serendipitously been trying to solve the same problem.</p>
<p>We talked after a local San Francisco Ruby meetup, at a bar of course (where all great ideas and partnerships begin), and decided to work on the problem together. I&rsquo;d been playing with an err-specific Gitweb, but Github is obviously much more ambitious in its scope.</p>
<p>As far as the adoption of Git, I have no idea what single thing tipped it. When we began working on the app in October, we were worried about ./script/plugin and tarballs and windows and svn mirroring. we weren&rsquo;t even sure we could build a business around Git, because subversion was still so utterly dominant.</p>
<p>As we all know, however, Git tipped at the start of 2008 and everyone somehow found the time to try it out. It maybe takes, like, two seconds to realize how much faster it is, and then a few more seconds after that to realize how much more awesome it is. So it&rsquo;s not much of a surprise that it is grabbing converts.</p>
<p>As for the Rails community, it&rsquo;s definitely because of the projects. When Merb, Rubinius, and Ruby on Rails all switch to Git, you have no choice. This is where the community is headed. As for other languages, we have a large number of non-ruby projects on Github. PHP, Java, Javascript, Lisp, Python — there are a handful of Django forks, for instance, the <a href="http://www.iolanguage.com/"target="_blank" rel="noopener">io</a> and <a href="http://programming.nu/"target="_blank" rel="noopener">nu</a> programming languages are also hosted on Github.</p>
<p>So really what it comes down to is early adopters, I think. Nu is a lisp written on Objective-C, very new and very cutting edge. The people using it right now are people who realize how powerful and awesome it is to run such a dynamic language on such a mature and stable platform. And naturally, the forward thinkers behind nu know that Git (and distributed source control) is an equally powerful, equally cutting edge concept.</p>
<p>With Prototype and Scripatculous moved over, and rumors of other Javascript frameworks switching, it&rsquo;s only a matter of time before Git becomes even more widespread.</p>
<p>I think the Railers just like to blog a lot.</p>
<p>As far as other solutions go, it&rsquo;s Rails vs Django all over again. If you use Mercurial, that&rsquo;s fine. The two are so similar (yes, <a href="http://kylecordes.com/2008/03/22/git-windows-works/"target="_blank" rel="noopener">Git does work great on Windows</a>) that as long as people move to distributed source control, it&rsquo;s still a win.</p>
<p>Oh, but wait, Mercurial doesn&rsquo;t have Github :-)</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/4/21/Picture_3.png" alt=""  loading="lazy" /></p>
<p><strong>AkitaOnRails:</strong> There you go :-) And I wonder what were the challenges of putting something as Github together. It is not a simple Rails web app, with very fast, pure Ruby code. You&rsquo;re probably dealing with thousands of system calls to Git command liners, several background jobs, maintenance, security. What do you think were the biggest challenges while assembling Github before recently releasing it to the public?</p>
<p><strong>Chris Wanstrath:</strong> Well you pretty much nailed it right there… scaling Rails is the easy part. scaling sshd, Git, Git-daemon, and our background jobs is the hard part.</p>
<p>We call Git directly many times per page, we need to process jobs that get entered from many different places (create a repo on the website, push a repo through ssh), we need to be secure (ssl and ssh for all private repos at all times), we need to make sure it all runs fast (memcached for both Git calls and the db), we need to make sure you&rsquo;re <em>&ldquo;in the loop&rdquo;</em> with a news feed aggregating information from all different sources within our system (almost 1m rows in the feed table when we launched).</p>
<p>So it definitely has not been easy. Not to mention that ssl is essentially the &ldquo;slow flag&rdquo; for http, so we need to make sure we&rsquo;re going fast because things like your dashboard already have a speed handicap. As an example, sshd stores its keys in an authorized_keys file. Well, we weren&rsquo;t even done with our beta yet and the keys file was over 4 megs of just plain text. So we had one of the C gurus at Engine Yard patch sshd for us to do mySQL based lookups, giving us faster searches and no pricey appending / writing of a massive file</p>
<p>Matthew Palmer is the said guru, and we plan to make that code open source in the near future.</p>
<p>We&rsquo;ve also got post-receive (when you &ldquo;Git push&rdquo;) hooks running that we host and deliver – so if you push, we&rsquo;ll POST to an arbitrary url for you with json of the commits. But you can also give us some info and we&rsquo;ll post to campfire, irc, twitter, or lighthouse for you with custom payloads.</p>
<p>Since we have flash, flash with haxe, Python, c, ruby, bash scripts, lots of Javascript, and some forthcoming erlang under the hood, it&rsquo;s definitely an interesting mix.</p>
<p>We are fortunate to be hosted on <strong>Engine Yard</strong> , though. If we didn&rsquo;t have them we&rsquo;d be screwed. Their awesome cluster setup with GFS means we can host our potentially 100s of gigs of repositories on a shared, raid&rsquo;d drive with redundancy and backups. If we were running Github on a barebones VPS, I dont even know how we&rsquo;d share access to those repos. It wouldn&rsquo;t be fun.</p>
<p>They also have a lot of experts available 24/7, which is great because I have a tendency to forget to sleep and work until 7am. A lot of the pain of running a heavily unix-dependent site is taken away by those guys.</p>
<p><a href="http://www.flickr.com/photos/ozmm/487496000/"target="_blank" rel="noopener"><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/4/21/Picture_5.png" alt=""  loading="lazy" /></a></p>
<p><strong>AkitaOnRails:</strong> Interesting, Erlang? I&rsquo;d like to know what you are up to with it. Going back, I think I saw a Rails trunk Git clone at Github way before DHH announced it a few days ago. I think it was Michael Koziarski who first did it, without much fuzz. Were you planning this together with the 37signals guys, or the switch of the Rails Core to Github was something that happened naturally? And do you have any kind of partnership with the Lighthouse guys as well, because you already have some hooks to manage Lighthouse tickets through Github commit messages, right?</p>
<p><strong>Chris Wanstrath:</strong> The Erlang stuff will be very cool, I promise. We&rsquo;ll make a big fuss about it when it&rsquo;s ready. As far as the Rails thing goes, koz did indeed have a mirror of his Git-svn repo at Github during the beta. It was unofficial. Then in february he wrote a <a href="http://www.koziarski.net/archives/2008/2/23/on-Git"target="_blank" rel="noopener">post explaining</a> that the core was thinking about moving to Git.</p>
<p>When I saw the post I emailed him and we talked a bit about what it would take to move Rails to Github, which was obviously something I love on a huge number of levels. He said he would get back to me, and then a few weeks later the Core team contacted us and wanted to discuss the move. The rest is history.</p>
<p>I really respect the work the <a href="http://www.lighthouseapp.com/"target="_blank" rel="noopener">Lighthouse</a> team does. The bug tracker is great, and the new redesign is even better, but we don&rsquo;t have any official partnership with them. We wrote the post-receive lighthouse hook because we wanted it, Rails wanted it, and a huge number of our users wanted it.</p>
<p><strong>AkitaOnRails:</strong> Many others started moving to Github during the Beta. I think Merb was more obvious because of Ezra&rsquo;s being involved in Engine Yard. Dr. Nic started using it for the Textmate bundles. Can you point to any other high profile Rails related projects hosted at Github right now?</p>
<p><strong>Chris Wanstrath:</strong> Merb was actually the reason Github went into beta. We launched the beta so they could start using the site for their 0.9 rewrite of merb-core. As far as popular projects, the best thing to do is check <a href="http://Github.com/popular/watched"target="_blank" rel="noopener">http://Github.com/popular/watched</a> and <a href="http://Github.com/popular/forked"target="_blank" rel="noopener">http://Github.com/popular/forked</a>. Datamapper, Rspec, and Mephisto are some of the popular ones.</p>
<p><strong>AkitaOnRails:</strong> Are you using vanilla Git code, or did you customize it in any way to better fit your environment? Have you ever talked to Junio Hamano or any other Git core maintainer? With Github probably eating up a lot of your time, how is Chow and FamSpam going right now? How are you managing all those products all at once?</p>
<p><strong>Chris Wanstrath:</strong> We&rsquo;ve patched Git-daemon to record statistics, which we&rsquo;ll soon be surfacing on the website soon in the form of better, more granular activity. You&rsquo;ll be able to see how many times your project was cloned, popular projects in the last 24 hours, all time most cloned projects, that sort of thing.</p>
<p>We&rsquo;ve emailed the core Git guys about the site, just to say thanks for writing Git and to let them know about Github, but haven&rsquo;t really had an occasion to chat with them about anything in particular. If I ever run into any of them, I&rsquo;ll definitely be buying them a few drinks, though.</p>
<p>I don&rsquo;t actually work at Chow anymore. My last day at C|NET was almost exactly one year ago, so my Ruby time is spent on FamSpam, Github, open source, and doing occasional client work.</p>
<p>FamSpam is going well. Now that Github is launched, there are a handful of features we want to get in and all. Luckily, however, the site is very polished and has been running smoothly without the need for much intervention. What we&rsquo;d really love to do, because it&rsquo;s really just a great Rails-based mailing list, is try out some different concepts.</p>
<p>Maybe one for open source, or a version for little league teams – anyone who needs to keep in touch and have their email workflow and document sharing streamlined. For right now, however, I try to spend as much time as possible on Github. There are so many places we want to take it.</p>
<p><strong>AkitaOnRails:</strong> I probably more than exceeded my interview time (sorry about that), but this conversation is super-interesting. One project I just remembered is Sake. To me Sake is very neat because many people complain about &ldquo;how difficult and complex&rdquo; the Git command line is, and Sake-like solutions make the workflow a lot easier. For instance, my entire Git workflow for Rails projects is only 4 small sake tasks like Git:update and Git:push. If I am not mistaken someone at Err did Sake, right? Are you still evolving it?</p>
<p><strong>Chris Wanstrath:</strong> Yeah, I wrote <a href="http://errtheblog.com/posts/60-sake-bomb"target="_blank" rel="noopener">Sake</a>. It&rsquo;s not a Rails plugin but it was written to help in Rails development. The &ldquo;Git tasks&rdquo; for Sake are becoming quite popular, as Git really just facilitates a better workflow. If you want to implement it, or different branches of that workflow, you can pretty easily just wrap up a few commands into a rake task or bash script.</p>
<p>And while bash scripts are great, sake tasks are portable. A lot of sake was inspired, very loosely, on Git – you can pull in sake tasks from any text file, whether it&rsquo;s on the web or local, and sake comes with its own daemon for serving your tasks.</p>
<p>So you could share tasks over wifi, or something, the same way you could share Git repositories over an adhoc network. Sake is up on Github, so feel free to add features you feel should exist.</p>
<p><strong>AkitaOnRails:</strong> Another thing I would like to hear your opinion about. I recently made a presentation on Rails Deployment strategies for beginners. The most common simple architecture revolves around Apache/Nginx/Litespeed + Mongrel/Evented Mongrel/Thin/Ebb. It is usually a matter of load balancing between a Web server using some kind of IPC to distribute the load back to Ruby VMs running your Rails app.</p>
<p>Now we have mod_Rails. What&rsquo;s your opinion on having all those choices, do you have any particular recipe you like or it depends on each application&rsquo;s needs? Did you have time to test mod_Rails? I think Hongli Lai is doing great stuff in regards to things like mongrel_light_cluster and his efforts on making Ruby&rsquo;s GC more copy-on-write friendly, did you take a look at that?</p>
<p><strong>Chris Wanstrath:</strong> The nice thing about Engine Yard is they get to take care of all that stuff for me. So while I used to spend a lot of time benchmarking and playing with different solutions, like Mongrel vs Emongrel, I just let the experts take care of it so I can focus on Github..</p>
<p>So no, I haven&rsquo;t played with mod_Rails yet because I&rsquo;m not in the situation where I could deploy it even if I wanted to use it. Which is in my opinion the best situation to be in, and pretty much the reason mod_Rails was written.</p>
<p><strong>AkitaOnRails:</strong> And by the way, are you going to make any presentation at RailsConf this year?</p>
<p><strong>Chris Wanstrath:</strong> I am. I&rsquo;ll be talking about <em>&ldquo;Beyond cap deploy&rdquo;</em>, which will pretty much be an in-depth look at the Github architecture, and then I&rsquo;ll be on a panel with Ben Curtis, Geoff Grosenbach, P.J., and Tom about being a &ldquo;profitable programmer&rdquo; w/ side projects.</p>
<p><strong>AkitaOnRails:</strong> I am very pleased to have the opportunity to talk to you.</p>
<p><strong>Chris Wanstrath:</strong> Yes, you too. Thanks!</p>
]]></content:encoded><category>interviews</category><category>open-source</category><category>software-architecture</category></item><item><title>Off-Topic: Google App Engine and Cloud Computing</title><link>https://akitaonrails.github.io/en/2008/04/13/off-topic-google-app-engine-e-cloud-computing/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2008/04/13/off-topic-google-app-engine-e-cloud-computing/</guid><pubDate>Sun, 13 Apr 2008 01:27:00 -0300</pubDate><description>&lt;p&gt;Just like &lt;em&gt;Web 2.0&lt;/em&gt;, another term thrown around all the time is &lt;strong&gt;Cloud Computing&lt;/strong&gt;. A lot of people use it to mean a lot of different things. Another term used as a synonym, without being exactly the same thing, is &lt;strong&gt;Web Services&lt;/strong&gt; (not the XML standard). In reality this is old news: it&amp;rsquo;s what we used to call ASPs (Application Service Providers).&lt;/p&gt;
&lt;p&gt;Think of services like Basecamp, for managing projects without the company spending on maintenance, or your favorite webmail. These are online services where you pay so you never have to think about infrastructure. A form of service outsourcing.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Just like <em>Web 2.0</em>, another term thrown around all the time is <strong>Cloud Computing</strong>. A lot of people use it to mean a lot of different things. Another term used as a synonym, without being exactly the same thing, is <strong>Web Services</strong> (not the XML standard). In reality this is old news: it&rsquo;s what we used to call ASPs (Application Service Providers).</p>
<p>Think of services like Basecamp, for managing projects without the company spending on maintenance, or your favorite webmail. These are online services where you pay so you never have to think about infrastructure. A form of service outsourcing.</p>
<p>This week Google caused a small stir by launching its answer to Cloud Computing: <a href="http://code.google.com/appengine/"target="_blank" rel="noopener">Google App Engine</a>. TechCrunch published a review <a href="http://www.techcrunch.com/2008/04/08/techcrunch-labs-our-experience-building-and-launching-app-on-google-app-engine/"target="_blank" rel="noopener">here</a>. But what is <a href="http://en.wikipedia.org/wiki/Cloud_computing"target="_blank" rel="noopener">Cloud Computing</a>? First things first, let&rsquo;s go over the terms the market uses.</p>
<p>We developers will, at some point, want to put our application into production. That&rsquo;s when the hairiest part of the process arrives: deployment!</p>
<p>There are several options. First, software:</p>
<ul>
<li>
<p>Buy a commercial application. Commodity cases like Exchange Server: you pick, buy, install, and start using it. Bigger applications sell under licensing schemes, per user or per processor. Some software can be extended and customized, but usually it&rsquo;s you who has to adapt to the software.</p>
</li>
<li>
<p>Get an open source solution. Download it from the internet, configure it, and use it, or hire a services company to do that work for you. The obvious advantage is zero license cost. The downside is that running that kind of setup has a cost nobody usually accounts for: <a href="http://en.wikipedia.org/wiki/Total_cost_of_ownership"target="_blank" rel="noopener">TCO</a> is not trivial. How easy the application is to modify varies case by case.</p>
</li>
<li>
<p>Build the system in-house. Code the system yourself, tailored to your needs, with your own team or a hired software house. The bespoke model. It tends to cost more, but it may be necessary when no commercial software does what you need. In theory it should be the software that fits the company best; in practice the TCO is usually high and the outcome uncertain. Everything depends on the maturity of the company and of whoever was hired to build it. Your mileage will vary.</p>
</li>
<li>
<p>Use an ASP. You &lsquo;rent&rsquo; a market application, like SalesForce.com, without worrying about deployment, installation, or updates. It&rsquo;s less customizable, but the total cost of ownership is usually far lower. In many cases it makes sense: all you need is a stable internet connection. The services model is a trend because today&rsquo;s internet infrastructure allows it. And there&rsquo;s a clear win for the developer: one fix in the central system instantly benefits the whole customer base, with no per-client installs.</p>
</li>
</ul>
<p>As for hardware options:</p>
<ul>
<li>
<p>Host it yourself. Buy machines, build networks, manage everything inside your own physical perimeter. As the company grows, you buy more machines. It works best for intranets and for companies with a mature IT department. IT is normally treated as a cost, like paying taxes; running everything in-house only makes sense if your core business is those very systems.</p>
</li>
<li>
<p>Host it yourself, but understanding that growth isn&rsquo;t linear. Sometimes you need more resources only for a short window, and buying machines all the time just inflates your TCO. That&rsquo;s what &lsquo;on-demand&rsquo; servers are for, like some IBM lines: when you need more power, you call IBM and they unlock more processors on the machine sitting physically at your site. A limited solution.</p>
</li>
<li>
<p>Co-location. The machines are yours, but they live on a third party&rsquo;s infrastructure, in a data center outside your company&rsquo;s perimeter. It&rsquo;s a cost-benefit trade: you rarely have the expertise to keep infrastructure as stable as a third party&rsquo;s, but the machines remain your responsibility. It fits companies that have the money for machines but no physical space, or branches that all need to reach the same system. The data center keeps everything running around the clock, with a stable network and 24/7 support.</p>
</li>
<li>
<p><em>Shared hosting</em>. You rent a piece of a machine shared with other clients. The entry cost is minimal, but you suffer when another client on the same box demands more resources, plus issues like shared system libraries. It&rsquo;s a can of worms that providers keep trying to fix, but the nature of this kind of package prevents anything bulletproof. Growing in such an environment is limited. It favors freelancers and small companies that need a website online but lack the money for beefier setups.</p>
</li>
<li>
<p><em>VPS</em> (Virtual Private Server). You still share the machine with other clients, but you get the feel of an entire machine of your own, virtual and isolated. A &lsquo;shared co-location&rsquo;: the machine is rented in slices, using solutions like Xen or VMware, and it comes out cheaper than co-location. The isolation shields your application from the neighbors&rsquo; updates. It&rsquo;s the second step for anyone who wants low cost with more flexibility. The problem remains when you need extra slices only for a short period: spinning one up takes time, and you can miss your time-to-market.</p>
</li>
</ul>
<h2>Cloud Computing<span class="hx:absolute hx:-mt-20" id="cloud-computing"></span>
    <a href="#cloud-computing" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Terms settled, on to the trendy one. I don&rsquo;t know if it was first, but the one making the most noise is certainly the <a href="http://www.amazon.com/aws"target="_blank" rel="noopener">Amazon Web Services</a> model: Elastic Compute Cloud (EC2), Simple Storage Service (S3), and SimpleDB.</p>
<p>The main draw is the <strong>elastic</strong> on-demand concept. You pay for what you use, without manually reconfiguring everything at each change. Use more, pay more; scale back down, pay less. It&rsquo;s payment for resource consumption, instead of a fixed monthly fee for fixed resources.</p>
<p>Amazon&rsquo;s products cover three services:</p>
<ul>
<li>
<p>Storage (Amazon S3). In a traditional model you&rsquo;d buy or rent a file server. S3 created a different kind of file system, based on the concept of &lsquo;buckets&rsquo;. The advantages are price and stability; the downside is a model that doesn&rsquo;t map directly onto a regular file system. By now there are dozens of libraries in different languages that ease the transition.</p>
</li>
<li>
<p>Database (Amazon SimpleDB). Instead of a relational Oracle or MySQL, Amazon offers an unstructured, document-based database. With no fixed schemas, the costly administration over your data domain disappears, and maintenance drops. On the other hand, there are still hardly any frameworks or libraries that migrate a relational (SQL) application to SimpleDB&rsquo;s model trivially. Once again, the advantage may be price.</p>
</li>
<li>
<p>VPS (Amazon EC2). Dynamic, elastic VPS instances. If your system demands more resources during a spike in visits and transactions, you spin up more slices dynamically and kill them when you no longer need them. Five slices in the morning, twenty in the afternoon, five again at night. The catch: they won&rsquo;t necessarily be the same five slices. The model guarantees slices, but not which ones. Your application must store its data outside EC2, in S3 or SimpleDB, so another slice can take over without relying on local data. It&rsquo;s Shared-Nothing taken to its ultimate conclusion.</p>
</li>
</ul>
<p>What&rsquo;s the advantage? You get what you need, when you need it. Instead of committing to expensive co-location or VPS plans, you leave the application in Amazon&rsquo;s care and gain dynamism without worrying whether your machines &lsquo;can take it&rsquo;. It&rsquo;s like paying for the gym by the hours you actually attended, instead of a fixed monthly fee. Quit mid-month, and you don&rsquo;t lose the whole fee. It&rsquo;s a trend in the services market.</p>
<p>What&rsquo;s the disadvantage? With Amazon, you have to prepare your application around their limitations and requirements. Especially because having your slice killed is normal: local data is gone if it wasn&rsquo;t persisted elsewhere first. Taken as a premise from day one, that stops being a problem. It&rsquo;s a different paradigm from co-location or classic VPS, where local data is permanent and backups are the host&rsquo;s job.</p>
<h3>Google App Engine<span class="hx:absolute hx:-mt-20" id="google-app-engine"></span>
    <a href="#google-app-engine" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Which brings us to the week&rsquo;s news. In practice, App Engine maps Amazon&rsquo;s services one to one:</p>
<ul>
<li>
<p>Amazon SimpleDB vs Google BigTable: neither is a relational database; forget the classic RDBMS.</p>
</li>
<li>
<p>Amazon S3 vs Google GFS: again, no classic file system like the one on your machine.</p>
</li>
<li>
<p>Amazon EC2 vs App Engine: similar, but different, as we&rsquo;ll see.</p>
</li>
</ul>
<p>Google&rsquo;s marketing sold the product as <em>&ldquo;your application running inside our infrastructure.&rdquo;</em> Everyone knows that Google Search, Reader, Gmail, and Orkut run on a proprietary, highly scalable infrastructure, the one that made Google famous and is now &lsquo;open&rsquo; (<a href="http://en.wikipedia.org/wiki/Gratis_versus_libre"target="_blank" rel="noopener">gratis, not libre</a>) for others to enjoy. Let&rsquo;s look at advantages and disadvantages:</p>
<ul>
<li>
<p>The downside on both sides is the paradigm break: applications must be born exclusive to one of these environments. Choosing one means locking yourself in, because supporting both costs more development.</p>
</li>
<li>
<p>Amazon&rsquo;s advantage is service independence: want just storage? Use S3 alone. The disadvantage is that their services demand more integration knowledge than anyone else&rsquo;s.</p>
</li>
<li>
<p>Google&rsquo;s disadvantage is the all-or-nothing proposition: your whole application lives in there, not just pieces. The advantage is the online administration environment, which puts Google a step ahead of Amazon in usability.</p>
</li>
</ul>
<p>Right now Google only supports applications written in Python. It ships with Django pre-available and with APIs that make handling data and storage easier. Certainly great news for the Python community.</p>
<p>That caused some <a href="http://web.archive.org/web/20090108161617/http://profy.com/2008/04/08/google-jumps-shark-with-app-engine/"target="_blank" rel="noopener">commotion</a> among developers of other languages, which is normal. Coming from anyone else, it would have passed unnoticed as &ldquo;just another hosting provider.&rdquo; Coming from Google, they know massive praise and massive criticism always follow. Praise is democratic, and so is criticism. If they didn&rsquo;t see this coming, someone in Public Relations needs to be fired.</p>
<p>Either way, don&rsquo;t pay attention to yet another round of language wars:</p>
<ul>
<li>
<p>they&rsquo;re a flash in the pan: only fun right now</p>
</li>
<li>
<p>they don&rsquo;t amount to much: if Google anticipated this and has good PR, they walk away unscathed</p>
</li>
<li>
<p>Google likes Python: that&rsquo;s public knowledge, and there&rsquo;s nothing wrong with it. Companies decide what they want, when they want, and how they want. The day you have your own company, you&rsquo;ll understand this.</p>
</li>
<li>
<p>and Google never said App Engine would be Python-exclusive</p>
</li>
</ul>
<p>In the official <a href="http://www.google.com/intl/en/press/annc/20080407_app_engine.html"target="_blank" rel="noopener">announcement</a>, Google presents the service as a &ldquo;preview release&rdquo;, the famous Beta of every self-respecting Web 2.0 application. Big news :-) The promise is to support other languages in the future. It&rsquo;s not a perfect solution, but like everything coming out of a corporation the size of Google, it has huge potential. And like every Web 2.0 application, there&rsquo;s a free account so you can test-drive the environment before deciding on a plan upgrade. At this Beta stage, though, it isn&rsquo;t open to everyone yet.</p>
<h2>What Does This Represent?<span class="hx:absolute hx:-mt-20" id="what-does-this-represent"></span>
    <a href="#what-does-this-represent" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Once App Engine matures and starts supporting more development environments beyond Python, it can become the main threat to Amazon&rsquo;s Web Services: it&rsquo;s a blatant copy of their services, and Amazon is the obvious commercial competitor.</p>
<p>It can also threaten smaller services, like <a href="http://web.archive.org/web/20081228161600/http://www.joyent.com:80/accelerator"target="_blank" rel="noopener">Joyent Accelerator</a>, a hosting provider built on the same elastic-resources concept, or the newcomer <a href="http://heroku.com/"target="_blank" rel="noopener">Heroku</a>, which at its core is an App Engine for Rails: a bridge over Amazon&rsquo;s services, configured in a simpler way for Ruby on Rails developers. Granted, it&rsquo;s not ready yet and is nowhere near Google&rsquo;s scale.</p>
<p>Neither Google&rsquo;s nor Amazon&rsquo;s services are perfect. Which is an obvious thing to say: no service is. Everything depends on your requirements and on how you want to commit. The rule stays the same: the more commitment, the cheaper in the short term and the pricier in the long run; the less commitment, the pricier now and, maybe, the better later.</p>
<p>In short: want something cheap and flexible? Amazon and Google can help. But if one day you grow and want independence, detaching can be hard, because your application got intimately tied to their services. With a more traditional service, like co-location or VPS, you pay more now, but you stay isolated from third parties and a future migration to other hosts comes cheaper.</p>
<p>If App Engine succeeds, the likely effect is pressure on the hosting market to offer more creative, cheaper, higher-quality services. That&rsquo;s market law: competition benefits us, the consumers. Let them fight it out while we watch. In the short term, for anyone wanting to put an application in the air, it&rsquo;s just one more variable to consider.</p>
<p>What&rsquo;s Google&rsquo;s goal here? I&rsquo;m not sure it&rsquo;s the same as the hosting companies&rsquo;: making money by outsourcing infrastructure in a commodity market. Google has specialized in turning good products into commodities, which is both good and bad, depending on your point of view. If it&rsquo;s a free service, one of the goals is to help more and more websites appear: potential customers for Google&rsquo;s real core business, AdSense.</p>
<p>Of course I wouldn&rsquo;t host my Twitter-equivalent on a free service. Actually, if your data matters to you, <strong>never</strong> use free services. They&rsquo;re great while they work; when they fail and you take a loss, the problem is uniquely and exclusively yours. It&rsquo;s like driving a car without insurance.</p>
<p>(off-topic within the off-topic: I use Gmail, but every email that lands in my inbox gets automatically forwarded to my paid Apple .Mac account, every .Mac email lands on my machine, and my machine has two redundant backups. Just to illustrate. I like Gmail, but I wouldn&rsquo;t bet even my emails on it.)</p>
<h2>Understanding Google<span class="hx:absolute hx:-mt-20" id="understanding-google"></span>
    <a href="#understanding-google" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>When analyzing a Google move, never think &lsquo;altruism&rsquo; or any such nonsense. They&rsquo;re a publicly traded company whose main objective is keeping shareholders happy. Again: their business is profit, and charity pays no dividends.</p>
<p>Their core business happens to involve reaching people who get happy hearing stock phrases like <em>&ldquo;Don&rsquo;t be evil.&rdquo;</em> Great for us, sure, but that also generates a conflict of interests.</p>
<p>(off-topic within the off-topic, no naivety: <a href="http://mashable.com/2008/04/10/orkut-pedophilia/"target="_blank" rel="noopener">not doing evil</a> is too vague, because &lsquo;good&rsquo; and &rsquo;evil&rsquo; are relative concepts. What&rsquo;s good for me can be bad for someone else, and vice versa; good and evil shift with place and time. Nobody will ever say &lsquo;do evil&rsquo;, obviously; that wouldn&rsquo;t be very smart. &lsquo;Doing good&rsquo;, or at least what the majority currently perceives as &lsquo;good&rsquo;, is a very intelligent way to generate positive publicity with consumer opinion. Besides being cheap, that kind of publicity can come with tax exemptions, which for big companies is considerable savings. Brazil&rsquo;s Lei Rouanet is an example: both sides win, but without laws like this there&rsquo;d be little motivation for a company to &lsquo;invest&rsquo; outside its core business. Bah, what do I know. And of course, I like Google too; so far it&rsquo;s in my interest.)</p>
<p>To understand it better, read <a href="http://web.archive.org/web/20081221135058/http://www.wired.com:80/wired/archive/11.01/google_pr.html"target="_blank" rel="noopener">this</a>. An interesting part:</p>
<blockquote>
  <p>&ldquo;Evil,&rdquo; says Google CEO Eric Schmidt, &ldquo;is what Sergey says is evil.&rdquo;</p>

</blockquote>
<p>Wired: &ldquo;moral compromise is just the cost of doing big business.&rdquo;</p>
<p>App Engine is one more channel for AdSense, just like Android and Gmail. One reason a company like Mozilla exists is the revenue line from Google for keeping Google search as the default in its toolbar. Apple also gets paid to have Google&rsquo;s search field in Safari. It doesn&rsquo;t quite count as a &lsquo;bundle deal&rsquo;, since both browsers are free, but the cost is indirect: AdSense advertisers want to appear in most browsers. Yahoo! also announced a <a href="http://web.archive.org/web/20080516124707/http://afp.google.com:80/article/ALeqM5jEgHRf-Zi_z785TetNNwtg2V5a9w"target="_blank" rel="noopener">test to show AdSense ads</a> on its own search results, which could increase Google&rsquo;s market share even further.</p>
<p>That means indirect visits, which turn into the billions of revenue that keep Google&rsquo;s shareholders happy. And geek users, who contribute the free word-of-mouth advertising, obviously love the idea that supporting Google or Firefox means <em>&lsquo;Microsoft losing&rsquo;</em>. I may be being pessimistic, but it happened with Rockefeller, a great philanthropist, and his Standard Oil. It happened with Bill Gates, another great philanthropist, and his Microsoft. It can happen with Google. To get an idea, read <a href="http://mashable.com/2008/04/10/orkut-pedophilia/"target="_blank" rel="noopener">this article</a> and an interesting excerpt:</p>
<blockquote>
  <p>Blogger and Mashable reader Constantine von Hoffman at Collateral Damage probably put it best when he said: &ldquo;At this point, it would take a mashup of Wittgenstein, Quantum mechanics and LSD to make sense of Google&rsquo;s various explanations for what it will and won&rsquo;t censor and why.&rdquo;</p>

</blockquote>
<p>Anyway, Google App Engine by itself represents no revolution yet: it&rsquo;s just one more player in the hosting market. Cloud Computing is one more marketing term for a solution derived from others that already existed: finer price granularity for services. Just as it did with Android, Google is commoditizing one more market segment.</p>
<p>The same old rule still applies: <a href="http://en.wikipedia.org/wiki/Cost-benefit_analysis"target="_blank" rel="noopener">cost-benefit analysis</a>. And that kind of analysis doesn&rsquo;t come as a recipe, nor with the click of a button.</p>
]]></content:encoded><category>cloud</category><category>software-architecture</category><category>off-topic</category></item><item><title>Chatting with Avi Bryant - Part 2</title><link>https://akitaonrails.github.io/en/2007/12/22/chatting-with-avi-bryant-part-2/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2007/12/22/chatting-with-avi-bryant-part-2/</guid><pubDate>Sat, 22 Dec 2007 20:36:00 -0200</pubDate><description>&lt;p&gt;If you didn’t read it, take a look at &lt;a href="https://akitaonrails.github.io/en/2007/12/15/conversando-com-avi-bryant-parte-1"&gt;Part 1&lt;/a&gt; where we get to know more about Avi Bryant and his amazing product &lt;a href="http://www.dabbledb.com"target="_blank" rel="noopener"&gt;Dabble DB&lt;/a&gt;. In &lt;strong&gt;Part 2&lt;/strong&gt; Avi goes a little bit more in elaborating his technology opinions and points of view. It’s a very insightful read for every programmer.&lt;/p&gt;
&lt;p&gt;As I always say – and Avi is good at pointing out —, Ruby has its drawbacks – most of them being improved on Ruby 1.9, JRuby and Rubinius. Avi gives us good reasons why Smalltalk is yet another great platform to learn, bringing back decades of evolution and maturity. So, here goes, the unabridged version of the interview.&lt;/p&gt;</description><content:encoded><![CDATA[<p>If you didn’t read it, take a look at <a href="/en/2007/12/15/conversando-com-avi-bryant-parte-1">Part 1</a> where we get to know more about Avi Bryant and his amazing product <a href="http://www.dabbledb.com"target="_blank" rel="noopener">Dabble DB</a>. In <strong>Part 2</strong> Avi goes a little bit more in elaborating his technology opinions and points of view. It’s a very insightful read for every programmer.</p>
<p>As I always say – and Avi is good at pointing out —, Ruby has its drawbacks – most of them being improved on Ruby 1.9, JRuby and Rubinius. Avi gives us good reasons why Smalltalk is yet another great platform to learn, bringing back decades of evolution and maturity. So, here goes, the unabridged version of the interview.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2007/12/19/539949594_f012918cd3.jpg" alt=""  loading="lazy" /></p>
<p>And stay tuned! I hope to have Evan Phoenix, Hal Fulton, Peter Cooper and Adrian Holovaty as my next guests. Lots of material to begin 2008 in great style.</p>
<p><strong>AkitaOnRails:</strong> Some people still try to make the case in favor of Smalltalk as it being the <em>“purest OO language out there”</em>. Even this being a fact, do you think that this alone is compelling enough to make a case against every other language, like Ruby which is kind of a multi-paradigm language? I hope you’re not pesky about ‘language pureness’ :-)</p>
<p><strong>Avi Bryant:</strong> Actually, I am pesky about it – not from an academic or aesthetic point of view, but from a pragmatic one. “Pure” languages make a lot of things easier: development tools, VMs, and infrastructure like distributed object systems or transparent persistence all get much simpler and better when you have a small, consistent language.</p>
<p>One experience I had, for example, was being hired to port my client library for the GOODS object database from Smalltalk to Python; I spent a huge amount of time dealing with special cases in the Python semantics (is this a class? a type? a new-style class? implemented in python or in C?) that simply never came up in Smalltalk.</p>
<p>Ruby does pretty well here, better than most in the scripting language space – I wouldn’t call Ruby “multi-paradigm”, by the way – but it could stand to do better.</p>
<p><strong>AkitaOnRails:</strong> I think you once said that you only consider a language “finished” when it is fast enough to extend itself. This is very true for Smalltalk, Java and other platforms. Ruby is undoubtedly lacking in its current position. That was one of the reasons we now have parallel efforts like John Lam’s IronRuby, Charles and Thomas’ JRuby and Evan’s Rubinius. One of the most criticized points is that Ruby doesn’t have a formal spec for the language or its core libraries. You also said that the Java VM is not suited enough for Ruby as Strongtalk would be, for example. Does this still hold today as we see JRuby 1.1 on the horizon?</p>
<p><strong>Avi Bryant:</strong> It still holds. JRuby currently seems to benchmark on par with MRI, and the best numbers from any Ruby implementation are maybe 3X MRI on average. I think we can do <strong>25X MRI</strong> for basic language feature benchmarks (message sends, block evaluation, etc), which should be enough to make Ruby implementations of the standard libraries feasible. Even if the net result was a similarly performing system, the side benefits would be extremely valuable.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2007/12/19/200126067_a2fc0a9fec.jpg" alt=""  loading="lazy" /></p>
<p><strong>AkitaOnRails:</strong> I remember that you started the <a href="http://rubyforge.org/projects/smalltalk/"target="_blank" rel="noopener">Smalltalk.rb</a> initiative a while ago, and if I am not mistaken it was an effort to make Ruby translate into equivalent Smalltalk code so it could run in any of the available Smalltalk VM implementations out there. Is this project still going on, do you still think it is feasible to pursue such a goal?</p>
<p><strong>Avi Bryant:</strong> I still think it’s a worthwhile and realistic goal. The project is still going in the sense that I’m still talking to people about how to make this happen, but I’m not able to spend time writing code for it myself at the moment. I have some reason to hope this project will progress more concretely next year, but we’ll see.</p>
<p><strong>AkitaOnRails:</strong> Some time ago I read a snippet of an IRC transcript between you, Evan, Chad and even Charles. You started talking about standardizing primitives (they are C-based in MRI today). My bet is that Rubinius is the project destined to be the “next big thing” for the Ruby community. To me it seems that you and Evan collaborate a lot, is that correct?</p>
<p><strong>Avi Bryant:</strong> I wouldn’t say that, but <a href="http://rubini.us/"target="_blank" rel="noopener">Rubinius</a> is certainly the Ruby project that most closely aligns with my own interests and goals at the moment, and it’s great to see how much effort is being put into it. Engine Yard also deserves a lot of kudos for their financial support of the project.</p>
<p>I have high hopes that the work Evan and crew are doing will be of great benefit not just to Rubinius but to all of the Ruby implementations – which is why I was advocating for a standard primitive set.</p>
<p><strong>AkitaOnRails:</strong> You know a lot of people, I wonder if you ever talked with Koichi Sasada himself to input some of your ideas for the YARV project as it’s going to be the “official” Ruby virtual machine out there a few months from now.</p>
<p><strong>Avi Bryant:</strong> No, I don’t believe we’ve ever spoken. I would certainly love to meet him some day.</p>
<p><strong>AkitaOnRails:</strong> Then, we get one level up to applications. Your Seaside framework is pretty amazing. You’re very right about <a href="http://developer.apple.com/tools/webobjects/"target="_blank" rel="noopener">WebObjects</a> as well. As a Java programmer I always pitied that it never got mainstream. Today it’s mostly relegated to Apple related websites, while lesser Java frameworks took the front. Tapestry and Cayenne are trying to top WebObjects’ features but seems they are nowhere near yet. Do you still program some Java? What is your opinion on current mainstream frameworks as Spring, JSF? For non-starters, what was it in WebObjects that makes it different than the rest?</p>
<p><strong>Avi Bryant:</strong> I don’t do any work in Java any more (thankfully), so I can’t really give a qualified opinion on the state of frameworks there right now. However, the main thing I think WebObjects did right was to focus on the state and behavior of the application, rather than on the mechanics of URLs and HTML and HTTP requests.</p>
<p>So rather than worry about what a field was named, you would just say <em>“this field is bound to this instance variable in my model”</em>, and rather than worry about what URL a link went to, you’d say <em>“this link triggers this method on my page object”</em>.</p>
<p>Transitioning between pages was done by constructing a new page object and setting it up directly in Java, rather than constructing a URL that was going to be parsed to build a page. That directness and general style is something that very heavily influenced Seaside.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2007/12/19/504096000_40e4e00b75.jpg" alt=""  loading="lazy" /></p>
<p><strong>AkitaOnRails:</strong> Many people may not immediately understand it as a word that is new to most of them: <em>“continuations”</em>. Meaning that instead of manually controlling the flow between events, with manual marshaling and unmarshaling of objects, you program as a cohesive and continuing flow. Can you explain this in a way that a novice would understand? What are the benefits and drawbacks of this approach?</p>
<p><strong>Avi Bryant:</strong> Well, it’s really all about being able to get modal behavior in a web app. In a desktop application, for example, you might occasionally have a call like <em>“file = chooseFile()”</em> or <em>“color = pickColor()”</em> that opens a modal dialog, gets a result from the user, and keeps going from there.</p>
<p>Seaside’s use of continuations is just to allow exactly that kind of interaction in the context of the web. The tricky bit is that because of the backbutton, the user might go pick a different color from the same dialog later, and the <em>“pickColor()”</em> call has to return a second time. This is confusing to think about, but mostly it Just Works.</p>
<p>From an implementation point of view, what we’re doing is saving a copy of the stack at the point the call is made so that we can come back to it later, more than once if need be.</p>
<p><strong>AkitaOnRails:</strong> You started these concepts in the Ruby world with IOWA. Once upon a time there was a framework called Borges that was also discontinued. Then Koichi mentioned taking continuation support from the next Ruby. Do you think the Ruby world is taking a step back with lack of support for continuations?</p>
<p><strong>Avi Bryant:</strong> Continuations are a great abstraction, and allow a lot of interesting experimentation with language semantics. However, I wouldn’t consider it a tragedy if they disappeared from Ruby; there are more important things to worry about. From a web perspective, I think that AJAX is starting to replace a lot of the use cases where having server-side modal logic used to be important – Dabble DB, for example, <strong>barely</strong> uses Seaside’s continuation features at all.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2007/12/19/106824107_67074eaf0b.jpg" alt=""  loading="lazy" /></p>
<p><strong>AkitaOnRails:</strong> Your blog has a peculiar name, <a href="http://www.cincomsmalltalk.com/userblogs/avi/View.ssp"target="_blank" rel="noopener">HREF considered harmful</a>. You probably got your inspiration from Edsger Dijkstra/Niklaus Wirth’s <a href="http://en.wikipedia.org/wiki/Considered_Harmful"target="_blank" rel="noopener">Goto Statement Considered Harmful</a> paper where Edsger made his famous case against GOTO. At the assembler level it is impossible to not JMP all the time (at least not without some macro help) but at a C level you can avoid GOTO’s altogether. Even Basic had both GOTO and GOSUB and you could just ignore the former. I am being simplistic here, but now you’re kind of saying that URLs play a similar role in a way that they disrupt the normal flow, going back in unpredictable ways and so on. On the other hand the Internet would not be what it is without URLs. How do we deal with this conundrum?</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2007/12/19/461207306_3f23831032.jpg" alt=""  loading="lazy" /></p>
<p><strong>Avi Bryant:</strong> I don’t think it’s a conundrum. I think your analogy is a good one: it’s impossible to not JMP all the time, and it’s also impossible not to use URLs all the time, but we don’t have to always be aware of either one. Back when everyone wrote in assembler, people probably obsessed about having meaningful or “pretty” JMP labels the same way they obsess about having pretty URLs today. I think we need to allow ourselves to make the same leap up in abstraction level on the web that we did with higher level programming languages.</p>
<p>That’s not to say we should <strong>never</strong> be thinking about URLs: any website needs to export meaningful URLs to be referenced and used externally, just like any C library needs to export meaningfully labelled function names so that it can be linked into other programs. But that’s as far as it needs to go.</p>
<p><strong>AkitaOnRails:</strong> Components were a big deal in the early 90’s, with RAD tools and so forth. Today it seems that they are showing up slowly, with initiatives as JSF and ASP.NET. Rails initially packed a very crude support for ‘components’ but it was dropped very early on as a bad thing (because of the jury rigged way it was implemented). Nothing new came to replace it what feels like there wasn’t enough need for it at the Rails level. On the other hand Seaside is heavy in componentization. Can you expose a little bit about the differences in this approach?</p>
<p><strong>Avi Bryant:</strong> “Component” is one of those terms like “MVC” or “open” that gets used to mean so many different things it’s hard to talk seriously about it. One concrete feature of Seaside’s approach is that each piece of UI is quite independent of what other pieces of UI may be on the page.</p>
<p>There’s no central controller which is responsible for request processing, choosing a template, or so on – all of those responsibilities are distributed among many smaller objects. There’s also no chance of naming conflicts because you don’t directly name links or form fields, so you can have many copies of the same form/widget/etc on the same page without any confusion.</p>
<p>This allows a level of modularity that’s very hard to reproduce in most traditional frameworks.</p>
<p><strong>AkitaOnRails:</strong> Another thing that might shock some people is that there is no traditional templating system in Seaside, no code mangling between HTML tags all over the place. Instead all HTML is abstracted in a object hierarchy that renders itself. In the Rails world there are a few alternatives that attempt some of that, like HAML, Markaby. What do you think are the fundamental differences?</p>
<p><strong>Avi Bryant:</strong> The HTML rendering API is a very core piece of Seaside, and a huge amount of the framework was designed with that underlying approach in mind. I think that’s the main difference when compared with HAML or Markaby, which are meant to be dropped into other frameworks.</p>
<p>Once you get rid of templates and switch to programmatic HTML generation, the design space just completely changes, and you need to re-evaluate almost every other decision that you made – this was a process that I went through with Seaside between the initial release, which used templates, and the 2.x versions, which don’t.</p>
<p>So I think we’ll have to see a framework that takes, say, Markaby as a starting point and asks “where can we go from here” before we can make any real comparisons.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2007/12/19/627957460_48e4181526.jpg" alt=""  loading="lazy" /><br>
with Adrian Holovaty, from Django fame</p>
<p><strong>AkitaOnRails:</strong> You also touted the shared-nothing vs share-all dilemma. And you obviously have a strong case because this is not just techno-babble but you do have a successful product running on top of that vision. Do you still think it pays off to sacrifice a little bit of performance for more clean code and productivity? In a way it’s sort of what DHH himself said about Rails when people said it was slower than PHP or Perl. Actually this also has to do with your talk about being a “heretic” in a way that you’re doing things in a different way than we all are now. Can you elaborate on that?</p>
<p><strong>Avi Bryant:</strong> <em>“Share nothing”</em> is great advice for making the maximum use of your server resources. For sites like yahoo.com or Facebook, that’s clearly very important because your business depends on serving millions of users every day.</p>
<p><em>“Share everything”</em> is really hard on your servers, but it makes maximum use of your developer resources. For a startup like Dabble DB, where we have 4 developers, that’s really important. And since our revenue model only requires serving thousands of users every day, not millions, it’s ok to spend a little more per user on server hardware. It’s been a very worthwhile tradeoff for us.</p>
<p><strong>AkitaOnRails:</strong> And speaking of DHH, you’ve probably met on several occasions. You’re both opinionated guys with antagonist visions. Smalltalk vs Ruby. Seaside vs Rails. Share-all vs Share-nothing. One could feel tempted to draw you two as Superman vs Batman kind of dispute. Though I know that smart people don’t fight over pesky stuff. What do you think of him and his ideas, how do you think both communities could cooperate if there’s any chance for that?</p>
<p><strong>Avi Bryant:</strong> I have a lot of respect for David. The only time I’ve felt antagonistic towards him was this last summer at Foo Camp. We were playing a game of <a href="http://en.wikipedia.org/wiki/Mafia_%28game%29"target="_blank" rel="noopener">Werewolf</a>, and I was a werewolf and he was a villager. Now, as a werewolf the best thing you can do is get the person nearest you to trust you, and that happened to be DHH. And it worked – Kevin Rose was totally on to me but David managed to persuade him not to lynch me, and I ate them both and won the game. Pn3d!</p>
<p>Anyway. There’s probably a lesson in there somewhere. Also, in a fight between me and DHH, <a href="http://en.wikipedia.org/wiki/Neal_Stephenson"target="_blank" rel="noopener">Neal Stephenson</a> would win.</p>
<p><strong>AkitaOnRails:</strong> Finally, Brazil still has a young and growing community. Any closing regards for our audience??</p>
<p><strong>Avi Bryant:</strong> Olá and good luck! I’ve been told Rio is even more beautiful than my home of Vancouver, and I hope to visit some day.</p>
<p><a href="http://twit.tv/floss21"target="_blank" rel="noopener"><img src="http://s3.amazonaws.com/akitaonrails/assets/2007/12/19/1057077803_3bd0155620.jpg" alt=""  loading="lazy" /></a><br>
<em>Don’t miss the TWiT podcast interview with Avi!</em></p>
]]></content:encoded><category>interviews</category><category>programming-languages</category><category>software-architecture</category></item><item><title>The Sunk Cost Fallacy</title><link>https://akitaonrails.github.io/en/2007/08/19/a-falcia-do-custo-perdido/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2007/08/19/a-falcia-do-custo-perdido/</guid><pubDate>Sun, 19 Aug 2007 00:16:00 -0300</pubDate><description>&lt;p&gt;&lt;img src="http://s3.amazonaws.com/akitaonrails/assets/2007/8/19/donkey.jpg" alt="Close-up of a donkey’s snout" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m buried in work (it&amp;rsquo;s 2 AM on a Sunday), but this little thought caught my attention because it&amp;rsquo;s simple and, even so, most people still fall for it. The &lt;a href="http://jchyip.blogspot.com/2007/08/sunk-cost-fallacy-for-architects.html"target="_blank" rel="noopener"&gt;original post&lt;/a&gt; is &lt;em&gt;Sunk Cost for Architects&lt;/em&gt;, but I think it should be &lt;em&gt;Sunk Cost for Managers&lt;/em&gt;. Here&amp;rsquo;s the translation:&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s say you&amp;rsquo;ve bought a movie ticket but then realise from &lt;a href="http://www.rottentomatoes.com/"target="_blank" rel="noopener"&gt;Rotten Tomatoes&lt;/a&gt; that the movie stinks. Assuming you can&amp;rsquo;t sucker one of your friends to buy the ticket, you have a couple choices:&lt;/p&gt;</description><content:encoded><![CDATA[<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2007/8/19/donkey.jpg" alt="Close-up of a donkey’s snout"  loading="lazy" /></p>
<p>I&rsquo;m buried in work (it&rsquo;s 2 AM on a Sunday), but this little thought caught my attention because it&rsquo;s simple and, even so, most people still fall for it. The <a href="http://jchyip.blogspot.com/2007/08/sunk-cost-fallacy-for-architects.html"target="_blank" rel="noopener">original post</a> is <em>Sunk Cost for Architects</em>, but I think it should be <em>Sunk Cost for Managers</em>. Here&rsquo;s the translation:</p>
<p>Let&rsquo;s say you&rsquo;ve bought a movie ticket but then realise from <a href="http://www.rottentomatoes.com/"target="_blank" rel="noopener">Rotten Tomatoes</a> that the movie stinks. Assuming you can&rsquo;t sucker one of your friends to buy the ticket, you have a couple choices:</p>
<ol>
<li>Since you&rsquo;ve paid for the ticket, you might as well suffer through the movie anyway.</li>
<li>Throw away the ticket and do something else.</li>
</ol>
<p>Now what?</p>
<p>The <a href="http://en.wikipedia.org/wiki/Sunk_cost"target="_blank" rel="noopener">sunk cost fallacy</a> is not realising that in either case, you&rsquo;ve already paid for the ticket and therefore the sunk cost is not relevant for rational decision making.</p>
<p>Case 1: You suffer the cost of the ticket + You suffer through the movie</p>
<p>Case 2: You only suffer the cost of the ticket</p>
<p>Therefore, rationally 2 is the better option.</p>
<p>So instead of a movie ticket, let&rsquo;s say it&rsquo;s an enterprise software license&hellip; or perhaps some custom hardware.</p>
<p>Should how much was paid for the license or hardware have any bearing whatsoever on what is decided to be the best future architectural approach?</p>
<p>Well, only if the management system you&rsquo;re operating in punishes rational behaviour and rewards irrational behaviour.</p>
<p>Total common sense. Economists are more used to studying and cataloguing behaviors like this. I recommend reading about other concepts like the good old <a href="http://en.wikipedia.org/wiki/Opportunity_Cost"target="_blank" rel="noopener">Opportunity Cost</a>. <a href="http://en.wikipedia.org/wiki/Parkinson%27s_Law"target="_blank" rel="noopener">Parkinson&rsquo;s Law</a>, to me, is the corollary of the gas expansion law, and every developer (myself included) has suffered from it.</p>
<p>Nobody&rsquo;s right all the time, but the daily exercise of thinking with logic and skepticism, instead of running on dogmas and precepts, improves our odds a lot. Maybe then, whoever watches <a href="http://en.wikipedia.org/wiki/A_Beautiful_Mind_%28film%29"target="_blank" rel="noopener">A Beautiful Mind</a> will remember more of the genius behind the <a href="http://en.wikipedia.org/wiki/Nash_equilibrium"target="_blank" rel="noopener">Nash Equilibrium</a> than the melodrama.</p>
]]></content:encoded><category>psychology</category><category>software-architecture</category><category>off-topic</category></item></channel></rss>