<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Arquitetura-De-Software | AkitaOnRails.com</title><link>https://akitaonrails.github.io/tags/arquitetura-de-software/</link><description>Blog do Fabio Akita do Canal do YouTube 'Akitando' falando sobre tecnologia, carreira e coisas geek.</description><generator>Hugo -- gohugo.io</generator><language>pt-BR</language><lastBuildDate>Mon, 19 Oct 2026 23:20:48 +0000</lastBuildDate><atom:link href="https://akitaonrails.github.io/tags/arquitetura-de-software/index.xml" rel="self" type="application/rss+xml"/><item><title>ai-memory: arquitetura emergente e software maleável</title><link>https://akitaonrails.github.io/2026/06/14/ai-memory-arquitetura-emergente-e-software-maleavel/</link><guid isPermaLink="true">https://akitaonrails.github.io/2026/06/14/ai-memory-arquitetura-emergente-e-software-maleavel/</guid><pubDate>Sun, 14 Jun 2026 14:00:00 -0300</pubDate><description>&lt;p&gt;Há umas três semanas eu publiquei o &lt;a href="https://akitaonrails.github.io/2026/05/23/criei-sistema-memoria-agentes-codigo-ai-memory/"&gt;post explicando o ai-memory&lt;/a&gt;, meu sistema de memória de longo prazo pra agentes de código. A ideia central, pra quem não leu: dar pro Claude Code, Codex, OpenCode e companhia um cérebro compartilhado que persiste entre sessões, uma wiki versionada em git com busca FTS, handoff entre agentes, e consolidação opcional via LLM. Cada projeto isolado, rodando local ou self-hosted, sem mandar seu contexto pra nuvem de ninguém.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Há umas três semanas eu publiquei o <a href="/2026/05/23/criei-sistema-memoria-agentes-codigo-ai-memory/">post explicando o ai-memory</a>, meu sistema de memória de longo prazo pra agentes de código. A ideia central, pra quem não leu: dar pro Claude Code, Codex, OpenCode e companhia um cérebro compartilhado que persiste entre sessões, uma wiki versionada em git com busca FTS, handoff entre agentes, e consolidação opcional via LLM. Cada projeto isolado, rodando local ou self-hosted, sem mandar seu contexto pra nuvem de ninguém.</p>
<p>Pois é. Em três semanas esse projetinho cresceu de um jeito que merece um post só sobre o <strong>processo</strong>, porque ele virou um exemplo vivo de uma coisa que eu prego há tempos: software de verdade é cultivado, igual planta. Ninguém monta software bom peça por peça como quem monta um Lego. Vou te mostrar os números, e principalmente a história que eles contam.</p>
<h2>Os números, hoje<span class="hx:absolute hx:-mt-20" id="os-números-hoje"></span>
    <a href="#os-n%c3%bameros-hoje" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O ai-memory tem hoje, em 24 dias de vida (do primeiro commit em 21 de maio até agora):</p>
<ul>
<li><strong>482 commits</strong> no <code>main</code>, uma média de 20 por dia.</li>
<li><strong>26 pessoas</strong> commitaram no repositório.</li>
<li><strong>10 crates</strong> Rust no workspace.</li>
<li><strong>~62,7 mil linhas</strong> de Rust de produção, mais <strong>~11 mil linhas</strong> de teste em <code>tests/</code>, mais de <strong>1.000 funções de teste</strong> e 90 módulos <code>#[cfg(test)]</code> espalhados pelo código.</li>
<li><strong>~19,7 mil linhas</strong> de documentação em markdown (<code>docs/</code>).</li>
<li><strong>70 pull requests</strong>, dos quais <strong>59 mergeados</strong> (84% de aceitação).</li>
<li><strong>27 issues</strong>, <strong>100% fechadas</strong>.</li>
<li>Saímos do <code>v1.0.0</code> em 12 de junho e já estamos no <code>v1.0.6</code>.</li>
</ul>
<p>Vinte e quatro dias. Guarda esse número, porque a parte interessante não é o tamanho final, é a forma como ele chegou aqui.</p>
<h2>Como começou: um só de tudo<span class="hx:absolute hx:-mt-20" id="como-começou-um-só-de-tudo"></span>
    <a href="#como-come%c3%a7ou-um-s%c3%b3-de-tudo" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O ai-memory nasceu pequeno e burro de propósito. Nos dois primeiros dias eu cravei o esqueleto inteiro: bootstrap do workspace, servidor MCP, captura por hooks, handoff entre agentes, versionamento da wiki em git, trait de provider de LLM, embeddings. Tudo isso rodando pra <strong>um usuário, uma máquina, um workspace, uma arquitetura</strong> (Linux, basicamente). Sem multi-usuário, sem permissão, sem escopo, sem suporte a Windows ou macOS. Era o meu cérebro de agente, na minha máquina, pra mim.</p>
<p>No dia 3, quando a <a href="https://github.com/akitaonrails/ai-memory"target="_blank" rel="noopener">primeira contribuição externa chegou</a> (um fix do Lucas Oliveira pra URLs de provider OpenAI-compatível, o PR #6), o projeto tinha <strong>102 commits e cerca de 22,9 mil linhas</strong> de código-fonte. Já eram os mesmos 10 crates de hoje, mas com uma fração da funcionalidade. Era um MVP de um-só-de-tudo que funcionava pra mim e mais ninguém.</p>
<p>E aqui é onde a maioria dos projetos morre, ou pior, onde a maioria dos programadores inexperientes erra a mão.</p>
<h2>A falácia do Lego<span class="hx:absolute hx:-mt-20" id="a-falácia-do-lego"></span>
    <a href="#a-fal%c3%a1cia-do-lego" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Tem uma crença muito difundida, principalmente em quem está começando, de que dá pra desenhar tudo que um software <strong>vai ser</strong> antes de escrever a primeira linha. Você senta, planeja a arquitetura inteira, quebra em pedacinhos bem organizados, com as pastas e as camadas e as interfaces todas previstas, e aí constrói peça por peça. Quando a última peça de Lego se encaixa, o software está pronto. Lindo no papel.</p>
<p>Na prática, é o caminho mais rápido pra deteriorar código. Quanto mais cedo você cristaliza uma estrutura, mais cedo ela vira uma camisa de força. Você previu um sistema de permissões elaborado antes de ter usuários? Parabéns, agora você carrega o peso de manter abstração que ninguém usa, e que quase sempre está errada, porque você a desenhou adivinhando o futuro em vez de responder a uma necessidade real. O programador inexperiente faz engenharia em excesso no começo, e não importa o quanto ele planeje, o resultado começa a apodrecer rápido, porque ele está resolvendo problemas que ainda não existem e errando os que existem.</p>
<p>Eu não acredito em software que termina. Já escrevi sobre isso no <a href="/2026/03/01/software-nunca-esta-pronto-4-projetos-a-vida-pos-deploy-e-por-que-one-shot-prompt-e-mito/">&ldquo;Software nunca está pronto&rdquo;</a>, e ele se conecta direto com as metodologias Agile originais, as de verdade, antes de virarem ritual corporativo de post-it. Software bom é uma escultura de barro: maleável, sempre úmida, que você fica ajustando, transformando, e que nunca fica pronta. É argila que você modela enquanto ela é usada. A torre de Lego que uma hora fica pronta e acabou é fantasia.</p>
<h2>O que os contribuidores construíram<span class="hx:absolute hx:-mt-20" id="o-que-os-contribuidores-construíram"></span>
    <a href="#o-que-os-contribuidores-constru%c3%adram" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>E é aí que entra a melhor parte de ter aberto o código. Aquele MVP de um-só-de-tudo virou um sistema multiplataforma e multiusuário porque <strong>muita gente</strong> colocou a mão. Eu quero agradecer nominalmente, porque sem essas pessoas o projeto seria uma fração do que é:</p>
<p><a href="https://github.com/djalmajr"target="_blank" rel="noopener"><strong>Djalma Júnior</strong></a> tocou boa parte da arquitetura pesada (multi-usuário, admin, recall cross-project), com 25 PRs mergeados. <a href="https://github.com/wblech"target="_blank" rel="noopener"><strong>wlech</strong></a> com dezenas de commits. <a href="https://github.com/pedrofjr"target="_blank" rel="noopener"><strong>Pedro de Freitas Jr</strong></a> em embeddings e correções de web. <a href="https://github.com/mrpaiva"target="_blank" rel="noopener"><strong>Mauro Couto de Paiva</strong></a> e <a href="https://github.com/CaTeIM"target="_blank" rel="noopener"><strong>Gustavo Cateim</strong></a> tocaram boa parte do suporte a Windows. <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> (autor do primeiro PR externo) e <strong>Said Rodrigues</strong>, entre vários outros. Valeu demais, gente.</p>
<p>O que essas contribuições adicionaram, em destaque:</p>
<ul>
<li><strong>Suporte real a Windows, macOS e Linux.</strong> O que era Linux-only ganhou compatibilidade com bash 3.2 do macOS, um índice de arquivos NTFS pra rastrear inode no Windows, e comandos de hook nativos por plataforma.</li>
<li><strong>Frontend API (<code>/api/v1</code>) e UI web customizável.</strong> Deixou de ser só MCP + CLI e ganhou uma superfície HTTP de verdade, com links de wiki clicáveis e busca por caminho de arquivo.</li>
<li><strong>Multi-usuário com atribuição.</strong> Tabela de <code>users</code>, comandos de writer/reader, e atribuição de autoria na mesma transação SQL da escrita (<code>pages.author_id</code> + frontmatter).</li>
<li><strong>Multi-workspace e recall cross-project.</strong> Busca global, links entre wikis de projetos diferentes, e isolamento de projeto ativo por sessão e por ator.</li>
<li><strong>Mais providers e mais agentes.</strong> Gemini, Copilot via OAuth, OpenAI-compat strict mode; e suporte a Antigravity, Grok e VS Code Copilot além dos originais.</li>
<li><strong>Robustez operacional.</strong> Recuperação por checkpoint, <code>reindex-from-wiki</code> pra reconstruir o índice a partir do markdown, manifestos <code>_meta.md</code> auto-descritivos.</li>
</ul>
<p>Repara numa coisa: nada disso estava no plano original. Não existia plano original. Cada uma dessas features entrou porque alguém precisou dela, abriu uma issue ou um PR, e a estrutura foi cedendo espaço pra acomodar. O multi-usuário não foi previsto: ele emergiu.</p>
<h2>E só agora a gente parou pra consolidar<span class="hx:absolute hx:-mt-20" id="e-só-agora-a-gente-parou-pra-consolidar"></span>
    <a href="#e-s%c3%b3-agora-a-gente-parou-pra-consolidar" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Aqui está o ponto central do post. Durante essas três semanas, o código cresceu meio bagunçado de propósito. Quando o suporte a multi-usuário entrou, a lógica de &ldquo;qual workspace e projeto esse pedido está mirando&rdquo; foi parar <strong>embutida direto no meio</strong> de vários handlers do servidor MCP, da API web, do admin, espalhada e duplicada. Cada lugar resolvia escopo do seu jeito. Tinha duplicação. Tinha código que não era bonito. E está tudo bem, porque estava funcionando, e funcionando pra muita gente.</p>
<p>Só <strong>depois</strong> que o software já rodava na máquina de dezenas de pessoas, em três sistemas operacionais, com multi-usuário de verdade em produção, é que eu parei e falei: &ldquo;ok, agora é uma boa hora pra refatorar e repensar a fiação interna&rdquo;. O penúltimo commit grande, o <a href="https://github.com/akitaonrails/ai-memory"target="_blank" rel="noopener"><code>refactor: centralize scope auth and wiki consistency</code></a>, fez exatamente isso, com 1.239 linhas adicionadas e 249 removidas, criando um <code>ScopeResolver</code> central de 601 linhas e um <code>ActorContext</code> consolidado.</p>
<p>Veja o &ldquo;antes&rdquo;. Esse trecho de resolução de escopo vivia duplicado, com variações, em vários métodos:</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">// ANTES: lógica de escopo embutida, repetida em cada 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>E o &ldquo;depois&rdquo;. A mesma resolução, agora delegada pra uma autoridade única no crate de store:</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">// DEPOIS: um resolvedor central, uma chamada
</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">// ... e cada handler vira isto:
</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>Três métodos diferentes que antes carregavam 30 linhas de lógica embutida cada um, com <code>lookup_ids</code>, fallback de workspace e tratamento de erro próprio, colapsaram em uma chamada cada. Junto vieram os commits irmãos: <code>harden multi-user admin auth boundaries</code> e <code>prevent scoped fallback bleed</code>. Centralizar permissão, escopo de workspace e usuário, e consistência da wiki, tudo num lugar só, com regra clara.</p>
<p>O detalhe que importa: o <code>ScopeResolver</code> não foi inventado no começo do projeto adivinhando que um dia ia ter multi-usuário. Ele foi <strong>extraído</strong> de um código que já existia, já funcionava, e já tinha mostrado, na prática, quais eram os padrões que se repetiam. A abstração veio depois do uso, não antes. Eu deixei a estrutura emergir do código, e só quando ela ficou óbvia eu a consolidei.</p>
<h2>A ordem certa: make it work, make it right, make it fast<span class="hx:absolute hx:-mt-20" id="a-ordem-certa-make-it-work-make-it-right-make-it-fast"></span>
    <a href="#a-ordem-certa-make-it-work-make-it-right-make-it-fast" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Esse é o resumo de tudo. Tem um ditado antigo, atribuído ao Kent Beck, que eu carrego como mantra: <strong>&ldquo;make it work, make it right, make it fast&rdquo;</strong>. Faça funcionar, depois faça direito, depois faça rápido. Nessa ordem. A maioria dos programadores inexperientes quer fazer &ldquo;direito&rdquo; e &ldquo;rápido&rdquo; antes de fazer &ldquo;funcionar&rdquo;, e aí inventa regra sem ter código pra justificar a regra.</p>
<p>O ai-memory seguiu a ordem certa sem eu nem pensar muito nisso. Primeiro fiz funcionar pra uma pessoa, escopo simples. Depois fiz funcionar uma feature de cada vez, refatorando e endurecendo o código no caminho, mas sem nunca cair na engenharia em excesso e sem nunca reescrever do zero pra encaixar numa estrutura pré-concebida. Eu deixei as estruturas atuais emergirem do código que foi sendo adicionado. Isso significou conviver com alguma duplicação e algum código meia-boca por um tempo, até bater aquele momento de &ldquo;agora sim é hora de refatorar e repensar a fiação&rdquo;. E só agora, com o software maduro e em uso, é que eu estabeleci padrões e regras melhores pra código novo. Regras lastreadas em código de verdade, não em adivinhação.</p>
<p>KISS, &ldquo;keep it simple&rdquo;, é sobre não resolver problema que você ainda não tem. Não tem nada a ver com escrever pouco código. É deixar a arquitetura emergir do código, e não o contrário. Você não desenha a escultura e despeja o barro dentro do molde. Você pega o barro, começa a modelar, e a forma aparece conforme suas mãos trabalham. O molde, se um dia precisar de um, você constrói depois, em volta da forma que já existe.</p>
<p>Vinte e quatro dias, 482 commits, 26 pessoas, e nenhum deles seguiu um diagrama desenhado no dia 1. Porque não existia diagrama no dia 1. E é exatamente por isso que funcionou.</p>
<p>O ai-memory é open source e continua mudando toda semana: <a href="https://github.com/akitaonrails/ai-memory"target="_blank" rel="noopener">github.com/akitaonrails/ai-memory</a>. Quer ajudar a modelar o próximo pedaço? Tem issue aberta esperando.</p>
]]></content:encoded><category>ai-memory</category><category>arquitetura-de-software</category><category>agile</category></item><item><title>RAG Está Morto? Contexto Longo, Grep e o Fim do Vector DB Obrigatório</title><link>https://akitaonrails.github.io/2026/04/06/rag-esta-morto-contexto-longo/</link><guid isPermaLink="true">https://akitaonrails.github.io/2026/04/06/rag-esta-morto-contexto-longo/</guid><pubDate>Mon, 06 Apr 2026 11:00:00 -0300</pubDate><description>&lt;p&gt;Já tem um tempo que essa coceira não me larga. No começo da onda de LLMs, lá em 2022/2023, a gente tinha 4k de contexto no GPT 3.5, 8k se esticasse, 32k era luxo. Pra fazer qualquer coisa com documento real, você não tinha escolha: tinha que recortar o texto em pedaços, gerar embeddings, jogar num vector database, fazer similarity search, pegar os top-5 chunks e rezar pra que os pedaços certos aparecessem.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Já tem um tempo que essa coceira não me larga. No começo da onda de LLMs, lá em 2022/2023, a gente tinha 4k de contexto no GPT 3.5, 8k se esticasse, 32k era luxo. Pra fazer qualquer coisa com documento real, você não tinha escolha: tinha que recortar o texto em pedaços, gerar embeddings, jogar num vector database, fazer similarity search, pegar os top-5 chunks e rezar pra que os pedaços certos aparecessem.</p>
<p>Daí virou indústria. Pinecone, Weaviate, Qdrant, Chroma, Milvus, pgvector, LangChain, LlamaIndex, Haystack. Tutorial em todo canto, &ldquo;construa seu chatbot com seus PDFs&rdquo;, consultorias inteiras vivendo disso. Virou meio que o &ldquo;hello world&rdquo; de LLM aplicado: documento → chunk → embed → vector DB → query.</p>
<p>Hoje, em abril de 2026, o Claude Opus 4.6 tem 1 milhão de tokens de contexto. O Sonnet 4.6, idem. O Gemini 3.1 Pro também. O GPT 5.4 fica numa janela menor mas ainda confortável, na casa das centenas de milhares. E pra alguns modelos já tem modos experimentais de 2M tokens. A pergunta que não me larga é: pra que cargas d&rsquo;água eu preciso montar uma stack vector pra resolver problema que cabe na janela do modelo?</p>
<p>E mais: vector database tem problemas reais que ninguém gosta muito de falar. Falsos vizinhos. Chunking arbitrário que parte definição do uso. Embeddings que envelhecem mal. Sem dizer que quando o resultado vem errado, você não tem a menor ideia do porquê.</p>
<p>A tese que eu venho amadurecendo é simples: na maioria dos casos, um <code>grep</code> bem feito mais uma janela de contexto generosa do modelo bate uma stack RAG completa. É mais barato, é mais fácil de manter, e quando dá pau você consegue debugar. Bora destrinchar.</p>
<h2>O que o vazamento do Claude Code mostrou<span class="hx:absolute hx:-mt-20" id="o-que-o-vazamento-do-claude-code-mostrou"></span>
    <a href="#o-que-o-vazamento-do-claude-code-mostrou" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Antes de entrar na parte teórica, vale falar de algo que aconteceu há poucos dias e que reforça muito essa discussão. Em 31 de março de 2026, a Anthropic, sem querer, publicou no npm a versão 2.1.88 do pacote <code>@anthropic-ai/claude-code</code> com um source map de quase 60 MB anexado, e cerca de 512 mil linhas de TypeScript da ferramenta interna deles vazaram pro mundo. Eu já tinha <a href="/2026/03/31/codigo-fonte-do-claude-code-vazou-o-que-achamos-dentro/">escrito sobre o incidente na semana passada</a>, com mais detalhe sobre o que apareceu no código.</p>
<p>O que interessa pra essa discussão é o sistema de memória do Claude Code. Em vez de jogar tudo num vector DB, a arquitetura tem três camadas. Um <code>MEMORY.md</code> que fica permanentemente carregado no contexto, mas não guarda dado nenhum: é só um índice de ponteiros, umas 150 caracteres por linha, mantido abaixo de 200 linhas e uns 25 KB. Os fatos de verdade ficam em &ldquo;topic files&rdquo; buscados sob demanda quando o agente precisa. E os transcripts brutos das sessões anteriores nunca são relidos inteiros, só pesquisados com grep atrás de identificador específico. Sem embedding. Sem Pinecone. Disciplina de escrita (topic file primeiro, índice depois) e busca lexical, só isso.</p>
<p>O loop principal do Claude Code também tem um sistema escalonado pra lidar com contexto enchendo. Como <a href="/2026/03/31/codigo-fonte-do-claude-code-vazou-o-que-achamos-dentro/">eu detalhei no post anterior</a>, são cinco estratégias diferentes de compactação de contexto, com nomes tipo <code>microcompact</code> (limpa resultados de tool antigos por tempo), <code>context collapse</code> (resume trechos longos da conversa), e <code>autocompact</code> (que dispara quando o contexto chega perto do limite). O CLAUDE.md, que muita gente pensava que era só uma convenção, é primeira classe na arquitetura: o sistema relê o arquivo a cada iteração da query.</p>
<p>O que isso me diz: a melhor ferramenta de coding agent que existe hoje, feita pela empresa que vende o modelo mais caro do mercado, <strong>não usa vector DB</strong>. Usa arquivos no disco, índice em markdown, busca lexical, e estratégias inteligentes de compactação quando o contexto estoura. Eles poderiam ter botado embedding, eles têm dinheiro pra rodar o que quisessem, e escolheram não. O motivo, na minha leitura, é exatamente o que esse post defende: pra recuperar texto de arquivos que você controla, com contexto generoso disponível, vector DB é peso morto. Melhor investir em compactar bem o que você já tem na janela do que indexar tudo num banco externo.</p>
<p>Tem um detalhe curioso de segurança que veio junto: a galera percebeu que o pipeline de compactação tem uma vulnerabilidade chamada de &ldquo;context poisoning&rdquo;. Conteúdo que parece instrução, vindo de um arquivo que o modelo lê (tipo um CLAUDE.md de um repo clonado), pode acabar sendo preservado pelo modelo de compactação como se fosse &ldquo;feedback do usuário&rdquo;, e o modelo seguinte segue isso como instrução genuína. É um vetor de ataque novo. Mas isso é assunto pra outro post.</p>
<h3>O sistema &ldquo;Dream&rdquo; e a consolidação de memória<span class="hx:absolute hx:-mt-20" id="o-sistema-dream-e-a-consolidação-de-memória"></span>
    <a href="#o-sistema-dream-e-a-consolida%c3%a7%c3%a3o-de-mem%c3%b3ria" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Mas o que mais me chamou atenção pro debate de RAG, e que eu já <a href="/2026/03/31/codigo-fonte-do-claude-code-vazou-o-que-achamos-dentro/">destrinchei na semana passada</a>, é o sistema chamado <code>autoDream</code>. É um subagente forkado, com bash read-only no projeto, que roda em background enquanto você não está usando a ferramenta. O job dele é literalmente sonhar: consolidar memória. O nome não é à toa, e a analogia óbvia (que eu não consegui evitar) é a do cérebro humano consolidando memória durante o sono, transformando experiência de curto prazo em conhecimento mais estável.</p>
<p>Pra um sonho rodar, três portas têm que abrir ao mesmo tempo: 24 horas desde o último sonho, no mínimo 5 sessões desde o último, e um lock de consolidação que impede sonhos concorrentes. Quando dispara, segue quatro fases. Orient (faz <code>ls</code> no diretório de memória, lê o índice). Gather (busca sinais novos em logs, memórias desatualizadas e transcripts). Consolidate (escreve ou atualiza os topic files, converte data relativa em absoluta, deleta fato que foi contraditado). E Prune, que é a faxina final que mantém o índice abaixo das 200 linhas.</p>
<p>A decisão de fazer o <code>autoDream</code> como subagente forkado é o detalhe que importa aqui. Ele não roda no mesmo loop do agente principal. Por quê? Porque consolidação de memória é processo barulhento. O modelo tem que reler transcript antigo, comparar com o que está no <code>MEMORY.md</code>, decidir o que fica e o que sai, fazer hipótese sobre coisa que viu em sessão anterior. Se isso rodasse no contexto principal, poluía o &ldquo;train of thought&rdquo; do agente que está tentando ajudar você na tarefa do momento. Forkando, separa as duas coisas. O agente principal continua focado no que você pediu, o <code>autoDream</code> faz a faxina em paralelo, sem permissão de escrita no projeto.</p>
<p>E o jeito como ele acha o que tem que consolidar é busca lexical, pura e dura. Os transcripts ficam em arquivos JSONL no disco, e o <code>autoDream</code> usa grep pra procurar sinais novos. Grep mesmo, em log de texto. Pensa nisso por um segundo. A consolidação de memória do agente mais avançado do mundo, feita por uma das empresas mais ricas de IA, é um subagente forkado fazendo grep em log de texto. Se vector DB fosse a resposta certa pra esse tipo de problema, a Anthropic tinha botado vector DB. Não botaram.</p>
<p>E tem um detalhe que pra mim é o ouro escondido do leak inteiro, e que cabe perfeitamente nesse argumento. No <code>autoDream</code>, memória é tratada como pista. O sistema parte do princípio de que o que está armazenado pode estar velho, errado, contraditado por algo que aconteceu depois, e o modelo tem que verificar antes de confiar. O pitch do vector DB é o oposto disso: indexa tudo, busca por similaridade, devolve top-k, confia no resultado. O Claude Code escolheu o caminho conservador. Indexa pouco, busca por palavra, devolve pista, e desconfia até bater o olho no fato real.</p>
<p>A estratégia inteira tem duas camadas. Dentro da sessão, contexto generoso mais grep mais compactação inteligente (<code>microcompact</code>, <code>context collapse</code>, <code>autocompact</code>). Entre sessões, um subagente que consolida memória de forma assíncrona, usando grep nos transcripts e tratando o resultado como dica, não como verdade. Embedding e vector DB não aparecem em nenhum dos dois lugares. A escolha consciente foi leitor inteligente comendo texto bruto, não leitor burro consumindo top-k de embedding.</p>
<p>A lição prática pro nosso debate é simples. Os agentes mais avançados do mercado tão indo na direção de contexto generoso, busca lexical e compactação inteligente, não na direção de pipeline RAG clássico. Se a Anthropic, com toda a infraestrutura e talento que tem, escolheu esse caminho pra Claude Code, a gente que tá construindo aplicação interna com uma fração do orçamento deveria pelo menos considerar a mesma direção.</p>
<h2>Onde a história começou a virar<span class="hx:absolute hx:-mt-20" id="onde-a-história-começou-a-virar"></span>
    <a href="#onde-a-hist%c3%b3ria-come%c3%a7ou-a-virar" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Quando o teto era 32k de contexto, retrieval era o gargalo do problema inteiro. Você tinha que pré-filtrar agressivo, porque qualquer coisa que entrasse na janela era espaço sagrado. Vector DB foi a única forma decente de fazer essa pré-filtragem semântica. A lógica era: &ldquo;o leitor (LLM) é caro e burro, então o retriever tem que ser inteligente e seletivo&rdquo;.</p>
<p>Hoje a equação virou. O leitor agora é o cara mais inteligente da mesa, e a janela cresceu pra caber documento inteiro. Aí o retriever pode (e talvez deva) voltar pra ser burro. Quanto mais burro, melhor. Você quer alta cobertura e baixa precisão, e deixa o modelo fazer a parte fina. Grep faz exatamente isso. BM25 também. E ripgrep voa em cima de milhões de linhas sem pestanejar.</p>
<p>E não é só achismo meu. Os benchmarks BEIR já mostraram faz tempo que BM25 bate ou empata com vários retrievers densos quando o domínio sai de onde os embeddings foram treinados. A própria Anthropic publicou um post sobre <a href="https://www.anthropic.com/news/contextual-retrieval"target="_blank" rel="noopener">Contextual Retrieval</a> basicamente dizendo a mesma coisa: sinal lexical mais julgamento de LLM bate embedding puro na maior parte das tarefas de conhecimento. E olha o Claude Code, a ferramenta que eu uso todo dia há 500 horas: ele navega o repositório com <code>Glob</code> e <code>Grep</code>. Sem vector DB, sem embedding, sem LangChain. Funciona ridiculamente bem.</p>
<h2>Os problemas reais do vector database que ninguém anuncia<span class="hx:absolute hx:-mt-20" id="os-problemas-reais-do-vector-database-que-ninguém-anuncia"></span>
    <a href="#os-problemas-reais-do-vector-database-que-ningu%c3%a9m-anuncia" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A propaganda de vector DB vende o sonho da busca semântica perfeita. A realidade é mais bagunçada.</p>
<p>Falsos vizinhos é o primeiro. Cosine similarity premia similaridade tópica, não relevância. Você pergunta &ldquo;como tratamos erro de autenticação&rdquo; e o DB devolve todo chunk que menciona autenticação. O chunk que efetivamente responde pode estar em décimo lugar, ou nem ter aparecido porque o redator do doc usou &ldquo;login&rdquo; em vez de &ldquo;auth&rdquo;.</p>
<p>Chunking é o segundo, e é um desastre disfarçado. Janela de 512 tokens, overlap de 64, parece razoável até você perceber que sua tabela importante foi cortada no meio, a definição de uma função ficou separada do uso, e o pedaço da documentação com o comando exato ficou órfão sem o contexto da seção. A fronteira do chunk costuma ser exatamente onde a resposta morava.</p>
<p>Quando falha, falha sem deixar pista. Quando o BM25 não acha, você sabe o porquê: a palavra não tá lá. Quando o vector DB devolve lixo, você recebe um chunk plausível e errado, sem nenhum sinal diagnóstico. Boa sorte debugando isso em produção às duas da manhã.</p>
<p>Índice envelhece. Cada update do documento pede re-embedding. Se você tem 10 mil docs e uns 200 mudam por dia, isso vira processo de batch, monitoramento, fila, retry, custo de API de embedding, e uma janela de inconsistência inevitável entre o que tá no disco e o que tá no índice. Grep não tem nada disso. Arquivo mudou? Próxima query já vê.</p>
<p>E tem o custo de operação que ninguém soma. Pinecone cobra por vector. Weaviate pede cluster pra manter. pgvector evita servidor novo mas você continua com schema, índice e pipeline de re-embedding. Cada uma dessas coisas pede tempo de engenheiro, monitoramento, teste, deploy. Tudo isso pra fazer uma busca que muitas vezes o <code>rg</code> resolve em 200ms.</p>
<h2>Comparando a complexidade<span class="hx:absolute hx:-mt-20" id="comparando-a-complexidade"></span>
    <a href="#comparando-a-complexidade" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olha o desenho:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/06/rag/rag-vs-grep-complexity.png" alt="Complexidade: RAG clássico vs grep &#43; contexto longo"  loading="lazy" /></p>
<p>De um lado, oito etapas, quatro ou cinco serviços, índice externo que precisa ser mantido e atualizado. Do outro, quatro etapas, zero infraestrutura nova. Não é caricatura: é literalmente o que você precisa montar pra cada caso.</p>
<p>A pergunta honesta é: a coluna da esquerda compensa? Em 2023, sim, porque a coluna da direita não existia (não tinha LLM com janela de 200k). Em 2026, na maior parte dos casos, não.</p>
<h2>Prós e contras de cada lado<span class="hx:absolute hx:-mt-20" id="prós-e-contras-de-cada-lado"></span>
    <a href="#pr%c3%b3s-e-contras-de-cada-lado" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>RAG clássico (vector DB)<span class="hx:absolute hx:-mt-20" id="rag-clássico-vector-db"></span>
    <a href="#rag-cl%c3%a1ssico-vector-db" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><strong>A favor:</strong></p>
<ul>
<li>Funciona pra bases de documento gigantes, da ordem de centenas de GB, onde nem <code>rg</code> resolve sem indexação prévia</li>
<li>Acerta consultas paráfrase pesada e cross-lingual (&ldquo;como cancelo&rdquo; vs &ldquo;encerramento de assinatura&rdquo;), onde o vocabulário do usuário não bate com o do documento</li>
<li>Funciona pra modalidades não-textuais (imagem, áudio) onde grep não tem o que olhar</li>
<li>Economiza tokens de input se você tá apertado de orçamento ou de latência absoluta</li>
</ul>
<p><strong>Contra:</strong></p>
<ul>
<li>Stack complexa: embedding, vector DB, chunking, reranker, pipeline de re-indexação</li>
<li>Falhas opacas, difíceis de debugar</li>
<li>Chunking destrói contexto de tabelas, código, definições longas</li>
<li>Overhead operacional (índice, fila, monitoramento, custo de re-embedding)</li>
<li>A busca semântica vendida no marketing raramente funciona como o marketing promete</li>
</ul>
<h3>Grep + contexto longo<span class="hx:absolute hx:-mt-20" id="grep--contexto-longo"></span>
    <a href="#grep--contexto-longo" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><strong>A favor:</strong></p>
<ul>
<li>Praticamente zero infraestrutura nova: ripgrep, sqlite, ou um simples <code>LIKE</code> em Postgres</li>
<li>Sempre fresco: o arquivo mudou, a próxima query já vê</li>
<li>Falhas transparentes: a palavra está ou não está</li>
<li>Carrega o documento em pedaços generosos, o modelo faz a filtragem fina com semântica de verdade</li>
<li>Mais barato em dev e ops, mais barato pra pivotar de domínio</li>
</ul>
<p><strong>Contra:</strong></p>
<ul>
<li>Não escala pra terabytes de texto bruto sem alguma indexação</li>
<li>Sofre quando o usuário usa vocabulário muito diferente do documento</li>
<li>Não funciona pra modalidade não-textual</li>
<li>Latência por query é maior em termos absolutos (carregar 100k tokens sempre custa mais que carregar 5k)</li>
<li>Custo de input por query é mais alto se você não tem prompt caching</li>
</ul>
<h2>Mas e o custo?<span class="hx:absolute hx:-mt-20" id="mas-e-o-custo"></span>
    <a href="#mas-e-o-custo" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Esse é o argumento que mais me jogam na cara quando defendo a tese do &ldquo;carrega tudo no contexto&rdquo;. &ldquo;Vai ficar caríssimo, 200k tokens de input por query é absurdo.&rdquo; Vamos fazer a conta de verdade.</p>
<p>No <a href="/2026/04/05/testando-llms-open-source-e-comerciais-quem-consegue-bater-o-claude-opus/">meu artigo do benchmark de LLMs de ontem</a> eu mapeei o preço por token de cada modelo. Pega o Claude Sonnet 4.6: $3 por milhão de tokens de input, $15 por milhão de output. Pega o GLM 5 (que provei que funciona): $0.60 input, $2.20 output. Pega o GPT 5.4 Pro lá em cima: $15 input, $180 output (esse aí machuca, eu sei).</p>
<p>Antes de fazer a conta de &ldquo;200k tokens&rdquo; em dólar, vale aterrissar isso em algo tangível, porque &ldquo;100k tokens&rdquo; não diz nada pra ninguém. Token, na média, é mais ou menos 0,75 palavra em inglês (em português é parecido, talvez um pouco mais por causa de palavras maiores). Então, traduzindo:</p>
<ul>
<li><strong>100k tokens</strong> ≈ 75 mil palavras ≈ um romance curto inteiro, tipo <em>O Velho e o Mar</em> do Hemingway com sobra, ou uns três artigos longos da Wikipedia juntos.</li>
<li><strong>200k tokens</strong> ≈ 150 mil palavras ≈ um romance grande, tipo <em>Crime e Castigo</em> na íntegra, ou metade do primeiro livro de <em>Game of Thrones</em> (que tem ~298k palavras, daria uns 400k tokens).</li>
<li><strong>400k tokens</strong> ≈ 300 mil palavras ≈ <em>A Game of Thrones</em> completo, livro 1 da série inteiro na janela.</li>
<li><strong>1M tokens</strong> ≈ 750 mil palavras ≈ a trilogia inteira de <em>O Senhor dos Anéis</em> mais <em>O Hobbit</em>, ou a Bíblia inteira (King James ~783k palavras, daria por volta de 1M tokens), ou cerca de dois livros e meio de <em>Game of Thrones</em> empilhados.</li>
</ul>
<p>Então quando eu falo &ldquo;joga 200k tokens de input no modelo&rdquo;, o que isso significa no mundo real é &ldquo;joga <em>Crime e Castigo</em> inteiro como contexto da pergunta&rdquo;. É muita coisa. E é exatamente isso que torna o argumento desse post viável: os modelos de hoje conseguem ler um romance inteiro de uma vez e ainda responder uma pergunta específica sobre ele. Em 2023, isso era ficção científica. Em 2026, virou o caso base.</p>
<p>Imagina então uma query que joga 200k tokens de input (lá vai <em>Crime e Castigo</em> de novo) e produz 2k tokens de output (umas três páginas de resposta):</p>
<table>
  <thead>
      <tr>
          <th>Modelo</th>
          <th style="text-align: right">Input ($)</th>
          <th style="text-align: right">Output ($)</th>
          <th style="text-align: right">Total por 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>Agora bota prompt caching no meio. O Claude tem cache que faz o input cacheado custar uma fração do preço cheio (na ordem de 10%, dependendo do modelo). O Gemini tem mecanismo similar. Quando você faz uma sequência de queries em cima do mesmo dump de 200k tokens, o custo das queries seguintes despenca pra centavos. Com Sonnet cacheado, dá pra falar em uns $0.10 por query subsequente sem inventar muito.</p>
<p>Agora compara isso com o custo de manter um Pinecone, ou um Weaviate, ou um pgvector. Ignorando o preço da assinatura em si (que varia bastante), você precisa de engenheiro pra montar a pipeline, manter, monitorar, lidar com falha de embedding, refazer chunking quando o domínio muda. Conservadoramente, fala em algo entre 40 e 80 horas de engenharia pra deixar a coisa estável. A R$ 200/hora, isso é entre R$ 8.000 e R$ 16.000. Em USD, na faixa de $1.600 a $3.200 só pra colocar de pé.</p>
<p>Com $3.200, no Sonnet 4.6 com prompt caching, você roda algo na ordem de 30 mil queries de 200k tokens. Trinta mil queries, dependendo da escala do projeto, dão vários meses ou até um ano inteiro de uma ferramenta interna média. E você não pagou engenheiro pra montar pipeline. Não tem servidor de vector DB pra manter. Se o documento mudar, o sistema já vê na próxima query.</p>
<p>A conta do &ldquo;RAG é mais barato em tokens&rdquo; ignora que token é a coisa mais barata da equação inteira. Engenheiro custa caro, servidor custa caro, bug em produção custa muito caro. Token virou commodity, e tá ficando mais barato a cada release de modelo novo.</p>
<p>O argumento clássico do RAG era &ldquo;modelo é caro, retrieval é barato&rdquo;. Hoje é o oposto: modelo é a parte barata da pilha, retrieval inteligente é o que sai caro pra montar e manter.</p>
<h2>Os pontos onde a tese não cola<span class="hx:absolute hx:-mt-20" id="os-pontos-onde-a-tese-não-cola"></span>
    <a href="#os-pontos-onde-a-tese-n%c3%a3o-cola" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Não quero parecer fanboy. Tem casos onde RAG clássico ainda ganha:</p>
<ol>
<li><strong>Bases gigantescas.</strong> Se você tem 500 GB de texto bruto, nem <code>rg</code> resolve em tempo aceitável. Aí precisa de algum tipo de indexação. Pode ser BM25 indexado (Tantivy, Elasticsearch), pode ser vector DB. Mas observa: a primeira opção ainda é lexical, não vetorial.</li>
<li><strong>Vocabulário muito disperso.</strong> Suporte ao cliente, onde o usuário fala &ldquo;tô sem net&rdquo; e a documentação fala &ldquo;perda de conectividade na camada física&rdquo;. BM25 não pega isso. Embedding pega. Aí vector DB ganha o ponto.</li>
<li><strong>Modalidade não-textual.</strong> Busca de imagem por imagem, áudio por áudio. Embedding é obrigatório.</li>
<li><strong>Latência absoluta crítica.</strong> Se você precisa responder em 100ms e tem 5k de orçamento de input, dump generoso não cabe. Aí pré-filtragem é necessária.</li>
<li><strong>Compliance e auditoria.</strong> Se você precisa provar que tal documento foi consultado pra responder tal query, ter chunks indexados rastreáveis ajuda. Dump de 200k de contexto é mais opaco em auditoria.</li>
</ol>
<p>Pra esses casos, RAG clássico continua fazendo sentido. Mas observa o tamanho da lista. São casos específicos. O caso geral, tipo &ldquo;chat com nossos documentos internos&rdquo; ou &ldquo;pergunte ao manual do produto&rdquo;, quase todo cai no balde do &ldquo;grep + contexto longo resolve melhor&rdquo;.</p>
<h2>Lazy retrieval: a receita que eu defendo<span class="hx:absolute hx:-mt-20" id="lazy-retrieval-a-receita-que-eu-defendo"></span>
    <a href="#lazy-retrieval-a-receita-que-eu-defendo" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Se eu fosse construir uma ferramenta de &ldquo;chat com docs&rdquo; hoje, do zero, seria mais ou menos assim:</p>
<ol>
<li><strong>Mantém os documentos brutos.</strong> Markdown, PDF convertido, código, o que for. No disco mesmo, organizado em pastas que façam sentido pro domínio.</li>
<li><strong>Filtro lexical rápido.</strong> <code>ripgrep</code> com regex, ou BM25 com Tantivy/SQLite FTS5, ou um <code>LIKE</code> no Postgres se já tiver. Devolve uns 100-300 hits.</li>
<li><strong>Carrega generosamente.</strong> Pega não só o trecho que bateu, mas o arquivo inteiro, ou uma janela grande em volta. Joga tudo no contexto.</li>
<li><strong>Deixa o LLM fazer a parte fina.</strong> Passa a pergunta original, manda o modelo encontrar o que importa, descartar o resto, e responder com citações.</li>
<li><strong>(Opcional) Adiciona embeddings só pra classes de query onde lexical falha</strong>, depois de você ter dados reais mostrando que falha.</li>
</ol>
<p>Isso é o oposto do conselho antigo (&ldquo;comece com vetores, fallback pra keyword&rdquo;). É: <strong>comece com keyword, e adicione vetor só se sentir falta</strong>. Na maioria dos projetos, você nunca vai sentir.</p>
<h2>Uma implementação de brinquedo em Ruby<span class="hx:absolute hx:-mt-20" id="uma-implementação-de-brinquedo-em-ruby"></span>
    <a href="#uma-implementa%c3%a7%c3%a3o-de-brinquedo-em-ruby" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Pra deixar concreto. Eis um script Ruby usando o gem <a href="https://github.com/crmne/ruby_llm"target="_blank" rel="noopener"><code>ruby_llm</code></a> (o mesmo do benchmark de ontem) que faz exatamente esse fluxo: grep nos arquivos, carrega os trechos com contexto, manda pro Claude, recebe a resposta. Sem vector DB, sem chunking, sem embedding, sem 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. Filtro lexical rápido com ripgrep.</span>
</span></span><span class="line"><span class="cl"><span class="c1">#    -i case insensitive, -l só nomes de arquivo, --type-add cobre md/txt/pdf-extraído.</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"># palavras com 4+ letras</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. Carrega os arquivos inteiros (até um teto razoável).</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. Manda pro Claude com a pergunta e os documentos.</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>São umas 40 linhas. Sem dependência de Pinecone, sem schema de vector, sem pipeline de re-indexação. Você roda como <code>./ask.rb ./docs &quot;como configurar o webhook do pagamento&quot;</code> e pronto.</p>
<p>Esse exemplo aí é one-shot. Você roda, ele responde, acabou. Pra chat de verdade, com várias perguntas em sequência em cima dos mesmos documentos, o desenho muda. Em vez de fazer o <code>lexical_search</code> lá no começo e empurrar tudo de uma vez pro contexto, você expõe a busca como tool pro modelo. Aí é o agente que decide quando precisa puxar mais doc, que termo vai procurar, qual arquivo vale a pena abrir inteiro. É assim que o Claude Code funciona, na real: <code>Glob</code>, <code>Grep</code> e <code>Read</code> são tools, e o modelo é quem escolhe a sequência. O <code>ruby_llm</code> suporta tool calling, então dá pra fazer a mesma coisa em Ruby. A cara fica mais ou menos assim:</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>O modelo recebe a pergunta, decide se precisa procurar, chama <code>search_files</code>, vê o que voltou, decide se precisa abrir algum arquivo, chama <code>read_file</code>, e só depois responde. Em pergunta seguinte ele já tem o contexto anterior na sessão e pode pedir mais coisa se precisar. O contexto vai recebendo só o que o modelo pediu, não o despejo inteiro do grep que o exemplo anterior fazia.</p>
<p>A mesma ideia funciona pra banco de dados: troca o <code>rg</code> por uma query SQL com <code>LIKE</code> ou <code>tsvector</code> (full-text do Postgres), carrega as linhas relevantes, joga no contexto. Se você tiver 10k registros num banco interno, isso resolve. Se tiver 10 milhões, aí você começa a precisar de paginação inteligente ou de uma camada de pré-filtragem mais séria. Mas a estrutura mental é a mesma: <strong>filtro burro + leitor inteligente</strong>.</p>
<h2>O ponto que importa<span class="hx:absolute hx:-mt-20" id="o-ponto-que-importa"></span>
    <a href="#o-ponto-que-importa" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O mais interessante nisso tudo nem é a economia de Pinecone. É que a natureza do gargalo mudou. Em 2023, o gargalo era retrieval: o leitor era pequeno, lento, caro, e você precisava de um retriever esperto pra encher a janela com o mínimo possível. Em 2026, o gargalo é raciocínio sobre contexto bagunçado: o leitor é grande, relativamente rápido, e barato. Aí faz mais sentido um retriever burrão de alta cobertura e deixar o modelo fazer o trabalho pesado.</p>
<p>Quem ainda projeta sistema com a cabeça de 2023 tá pagando caro pra resolver um problema que mudou de forma. RAG não morreu, o &ldquo;R&rdquo; ficou mais burro e mais barato, e isso é uma melhoria. Quem vende vector DB não vai te contar, mas é o caminho que a galera mais experiente vem seguindo na surdina.</p>
<p>A próxima onda de aplicação LLM, na minha aposta, vai ser dominada por quem entendeu essa inversão. Stack menor, infraestrutura mais simples, contexto generoso, e muito menos LangChain.</p>
<h2>O que a literatura recente diz<span class="hx:absolute hx:-mt-20" id="o-que-a-literatura-recente-diz"></span>
    <a href="#o-que-a-literatura-recente-diz" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Antes de fechar, fui dar uma olhada no que a galera de pesquisa publicou sobre isso. Achismo de blog nessa área envelhece em três meses, então melhor olhar paper.</p>
<p>O <a href="https://arxiv.org/abs/2407.16833"target="_blank" rel="noopener"><strong>Retrieval Augmented Generation or Long-Context LLMs?</strong></a>, do Google DeepMind, publicado na EMNLP 2024, é o mais citado no debate. A conclusão deles: quando o modelo tem recurso suficiente, long context bate RAG na média de qualidade, mas RAG continua sendo bem mais barato em tokens. Eles propõem o Self-Route, uma abordagem onde o próprio modelo decide se precisa do retrieval ou se manda direto pelo contexto. A economia de tokens é grande e a perda de qualidade é pequena.</p>
<p>Já o <a href="https://openreview.net/forum?id=CLF25dahgA"target="_blank" rel="noopener"><strong>LaRA</strong></a>, apresentado na ICML 2025, é mais comedido. Os autores montaram 2326 casos de teste em quatro tipos de tarefa de QA e três tipos de contexto longo, rodaram em 11 LLMs diferentes, e a conclusão foi: não tem bala de prata. A escolha entre RAG e long context depende do modelo, do tamanho do contexto, do tipo de tarefa e da característica do retrieval. RAG ganha em diálogo e queries genéricas, long context ganha em QA estilo Wikipedia.</p>
<p>O <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>, de janeiro de 2025, é o que mais reforça a tese deste post. Long context costuma bater RAG nos benchmarks de QA, especialmente quando o documento base é estável. Retrieval baseado em sumarização chega perto, e retrieval baseado em chunk fica atrás. Ou seja: o jeito antigo, chunk mais embed mais top-k, é o que sai pior.</p>
<p>Tem que ter no radar também o original <a href="https://arxiv.org/abs/2307.03172"target="_blank" rel="noopener"><strong>Lost in the Middle</strong></a> (Liu et al., 2023, publicado no TACL em 2024). Foi o paper que mostrou que mesmo modelos com janela grande têm performance dependente da posição da informação relevante. Coisa no começo ou no fim do contexto é encontrada fácil; coisa no meio degrada. Por muito tempo isso foi usado como argumento contra long context, mas o paper é de 2023, com modelos de 2023. Os modelos de hoje, tipo Claude 4.x e Gemini 3.x, lidam muito melhor com a parte do meio. Não é problema resolvido, mas é bem menor do que era.</p>
<p>Pelo lado de retrieval lexical, o <a href="https://arxiv.org/abs/2104.08663"target="_blank" rel="noopener"><strong>BEIR</strong></a> continua sendo a referência canônica. O resultado clássico é que BM25, lá dos anos 90, segue competitivíssimo em cenário out-of-domain. Os modelos densos só ganham consistentemente quando você tem dados do próprio domínio pra fine-tunar os embeddings. Em cenário zero-shot, que é onde a maioria dos projetos vive, BM25 é difícil de bater sem trabalho pesado.</p>
<p>Pra fechar, o post da <a href="https://www.anthropic.com/news/contextual-retrieval"target="_blank" rel="noopener"><strong>Anthropic sobre Contextual Retrieval</strong></a>, de setembro de 2024, é a peça mais prática da lista. Eles mostram que combinando embedding contextualizado com BM25 contextualizado, dá pra cair de 5.7% pra 2.9% de taxa de falha no top-20. Adicionando reranker, cai pra 1.9%. Detalhe importante: BM25 é peça central do resultado deles, não auxiliar. A leitura correta é &ldquo;lexical mais vetor mais reranker é a combinação que funciona&rdquo;. Quem só pode escolher um, escolhe BM25 e ainda chega longe.</p>
<p>Resumindo o que dá pra cravar: a literatura não diz que &ldquo;RAG morreu&rdquo;. Diz que long context, quando dá pra usar, costuma vencer em qualidade. Diz que o custo de RAG ainda é o argumento principal a favor dele. Diz que BM25 lexical é bem mais forte do que a propaganda de vector DB faz parecer. E diz que, quando você realmente precisa de retrieval pesado, a combinação robusta é hybrid (lexical mais vetor mais reranker), não vetor puro. Tudo isso bate com o que eu venho defendendo na prática.</p>
<h2>Fontes<span class="hx:absolute hx:-mt-20" id="fontes"></span>
    <a href="#fontes" 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 técnico.</li>
<li>Akita, F. (2026). <a href="/2026/03/31/codigo-fonte-do-claude-code-vazou-o-que-achamos-dentro/">O código fonte do Claude Code vazou. O que achamos dentro.</a> — minha cobertura do leak, com mais detalhe sobre arquitetura de memória, KAIROS e <code>autoDream</code>.</li>
</ul>
]]></content:encoded><category>llms</category><category>arquitetura-de-software</category><category>bancos-de-dados</category></item><item><title>[Akitando] #144 - Modelagem de Software é Difícil? | "Ver" vs "Enxergar"</title><link>https://akitaonrails.github.io/2023/08/11/akitando-144-modelagem-de-software-e-dificil-ver-vs-enxergar/</link><guid isPermaLink="true">https://akitaonrails.github.io/2023/08/11/akitando-144-modelagem-de-software-e-dificil-ver-vs-enxergar/</guid><pubDate>Fri, 11 Aug 2023 15:57:00 -0300</pubDate><description>&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/SmYQcaP1Jgw"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Um dos assuntos que mais assusta iniciante é esse negócio de &amp;ldquo;modelagem de software&amp;rdquo; ou &amp;ldquo;design de software&amp;rdquo; ou &amp;ldquo;arquitetura de software&amp;rdquo;. Vários nomes que parecem ser a mesma coisa e ao mesmo tempo não serem coisa nenhuma.&lt;/p&gt;
&lt;p&gt;Hoje quero &amp;ldquo;filosofar&amp;rdquo; um pouco pra dar uma perspectiva diferente de como se deve encarar esses assuntos e talvez uma estratégia de como realmente entender como isso funciona.&lt;/p&gt;</description><content:encoded><![CDATA[

<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/SmYQcaP1Jgw"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>Um dos assuntos que mais assusta iniciante é esse negócio de &ldquo;modelagem de software&rdquo; ou &ldquo;design de software&rdquo; ou &ldquo;arquitetura de software&rdquo;. Vários nomes que parecem ser a mesma coisa e ao mesmo tempo não serem coisa nenhuma.</p>
<p>Hoje quero &ldquo;filosofar&rdquo; um pouco pra dar uma perspectiva diferente de como se deve encarar esses assuntos e talvez uma estratégia de como realmente entender como isso funciona.</p>
<h2>CAPÍTULOS<span class="hx:absolute hx:-mt-20" id="capítulos"></span>
    <a href="#cap%c3%adtulos" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li>00:00 - Intro</li>
<li>02:08 - CAP 1 - Livros de Modelagem e Arquitetura - Devo Estudar Todos?</li>
<li>04:14 - CAP 2 - O Problema dos Tutoriais - Modelando Objetos Reais?</li>
<li>13:07 - CAP 3 - Modelando o Mundo Real - Um Exemplo</li>
<li>16:04 - CAP 4 - A diferença de &ldquo;VER&rdquo; e &ldquo;ENXERGAR&rdquo; - Michelângelo e Arte</li>
<li>21:30 - CAP 5 - Mentoria do Mundo Real - Cuidados</li>
<li>24:44 - CAP 6 - Analisando o Mundo Real - Aprendendo Ecommerce</li>
<li>33:11 - CAP 7 - Teoria e Prática - A História do Magento</li>
<li>41:07 - CAP 8 - Recomendação de Livros - Aprendendo com Especialistas</li>
<li>49:17 - Bloopers</li>
</ul>
<h2>Links<span class="hx:absolute hx:-mt-20" id="links"></span>
    <a href="#links" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li><a href="https://github.com/magento/magento2"target="_blank" rel="noopener">Magento 2</a></li>
<li><a href="https://github.com/spree/spree"target="_blank" rel="noopener">Spree</a></li>
<li><a href="https://github.com/morinoko/agile-dev-rails6"target="_blank" rel="noopener">Agile Web Development with Rails - GitHub</a></li>
<li><a href="https://pragprog.com/titles/rails7/agile-web-development-with-rails-7/"target="_blank" rel="noopener">Agile Web Development with Rails</a></li>
<li><a href="https://aosabook.org/en/"target="_blank" rel="noopener">The Architecture of Open Source Applications</a></li>
<li><a href="">Rails Summit 2009 - Ilya Grigorik</a></li>
<li><a href="https://www.akitaonrails.com/2009/11/18/conversando-com-luke-kanies"target="_blank" rel="noopener">Chatting with Luke Kanies</a></li>
<li><a href="https://www.akitaonrails.com/2009/05/08/railsconf-09-entrevistas-exclusivas-em-audio/"target="_blank" rel="noopener">RailsConf 09 - Exclusive Audio Interviews | AkitaOnRails.com</a></li>
</ul>
<h2>SCRIPT<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>No último video resolvi falar mais sobre bancos de dados. Já tinha feito dois videos antes sobre isso e em todos fui pelo caminho mais fácil: explicar como funcionam por baixo. As engrenagens que possibilitam existir armazéns de dados. Mas explicar implementação não é tão difícil, porque eles já existem e funcionam bem.</p>
<p>O problema que eu venho me esquivando desde o começo do canal é explicar o que a maioria de nós, programadores, considera o problema mais difícil da programação: dar nomes pras coisas. Isso mesmo, não é piada. Em particular, como modelar um problema de negócios em código? O que é um design de software &ldquo;feito direito&rdquo;? O que significa &ldquo;fazer direito&rdquo;? Significa seguir Domain Driven Development? Significa usar a maioria dos Design Patterns? Significa seguir algum framework? Tem algum livro que se possa ler e aprender o que significa isso de &ldquo;modelar&rdquo;?</p>
<p>Durante toda a vida deste canal eu vim flertando com esse assunto, por exemplo quando falei do Livro The Mythical-Man Month, que afirmei ser o mais importante pra se ler. O video de hoje vai ser uma filosofada super longa e provavelmente super frustrante. Frustrante porque eu não vou dar copy e paste de material que já tem online. É só procurar que você acha tudo de patterns, arquitetura, modelagem. O objetivo do video é explicar porque muitos acham &ldquo;difícil&rdquo;, mesmo tendo todo esse material. E no final vou até recomendar outros materiais, então assistam tudo.</p>
<p>O spoiler é que não existe o &ldquo;jeito certo&rdquo; de se fazer software, mas por outro lado, o processo não é aleatório. Existe um balanço fino entre teoria e prática que é onde se encontra nossa profissão. A razão de existir programadores é justamente porque é nossa função decidir o que é &ldquo;melhor&rdquo; pra determinada circunstância.</p>
<p>Lembram do video de Não Terceirize Suas Decisões? O pior programador é aquele que é incapaz de tomar decisões. O melhor programador não é aquele que toma todas ou a maioria das decisões corretas, mas sim aquele que toma decisões possíveis no momento em que precisam ser tomadas, nem antes, e nem depois. E o que vou tentar fazer hoje é ilustrar esses conceitos. Então vamos lá.</p>
<p>(&hellip;.)</p>
<p>Como primeira opinião controversa do dia, vou afirmar o seguinte. A maioria dos livros de modelagem e design, como o Clean Code do Bob Martin, o Domain Driven Development do Eric Evans, o Design Patterns do Gang of Four, o Patterns of Enterprise Application Architecture do Martin Fowler; todos livros excelentes, mas eu acho que são totalmente inúteis no seu primeiro ano pós faculdade. Entenda: você acabou de se formar, ainda tem pouca ou nenhuma experiência em projetos reais ou em projetos pessoais ou open source. Esses livros são perda de tempo.</p>
<p>Pra ficar muito claro: eu não estou dizendo que os livros são inúteis. Muito pelo contrário, eu acho todos eles excelentes e que precisam ser estudados. Mas todos tem um pré-requisito: que você já tenha quilometragem em código. Tanto em fazer seu próprio código quanto lidar com código dos outros, especialmente código legado.</p>
<p>A estratégia é assim: nenhum desses livros você deve tentar ler de cabo a rabo de uma só vez. Folheie alguns capítulos, os iniciais. Veja os temas de cada um pelo índice, leia a primeira página de cada capítulo. Guarde. Vá trabalhar ou treinar com projetos open source. Volte daqui um ou dois meses. Folheie de novo, avance um capítulo a mais, pule pra um capítulo no meio que tem assunto que chamou sua atenção porque é compatível com o que está mexendo no momento. Guarde de novo.</p>
<p>Depois do primeiro ano trabalhando tempo integral com programação, retorne. Leia com um pouco mais de esforço. Releia os primeiros capítulos. Repita. É um processo que deveria te custar os primeiros 3, talvez 5 anos iniciais de carreira. Se fizer direito, talvez comece a entender o que esses autores queriam dizer quando estiver lá pelo quarto ano.</p>
<p>Um dos sinais que você conseguiu mesmo pular de nível junior pra pleno, é quando começa a entender o que os autores queriam dizer, e não está tentando seguir ao pé da letra o que está escrito, sem saber porque. Mas no primeiro ano, esses livros são inúteis. Você ainda não lidou com problemas suficiente pra entender esses porquês. E o importante é o porquê. Agora deixa eu dar uns passos pra trás e explicar porque.</p>
<p>Qualquer um que já tenha tido curso ou lido livro sobre modelagem de objetos já deve ter esbarrado no famoso exemplo dos animais ou veículos. Por exemplo, ambos cachorro e gato são animais. Animal é uma abstração. Portanto, parece óbvio que deveríamos fazer uma classe abstrata chamada Animal, daí podemos derivar sub-classes Cachorro e Gato que herdem de Animal, ou em linguagens mais modernas, que tenham o &ldquo;trait&rdquo; ou protocolo de Animal.</p>
<p>Ou então temos Carro e Caminhão, ambos são veículos, poderia ser essa a classe pai abstrata. Carro e Caminhão herdam de veículo. O problema com todos esses exemplos, primeiro é que eu nunca vi ninguém fazer uma classe realmente chamada Animal. Talvez quem já trabalhou em projetos de biologia ou zoológico. Mesmo se fizer um sistema de uma fábrica ou concessionária de carros, provavelmente vamos ter uma tabela ou classe mais genérica, mais abstrata ainda, como &ldquo;Produto&rdquo;.</p>
<p>O problema de sistemas de verdade é que praticamente todos os conceitos mais importantes são abstratos, não concretos. Por exemplo, o caso mais óbvio, que são os usuários de um sistema. Usuários não são pessoas? Acho que é por isso que todo iniciante começa um sistema pela tabela de usuários, porque é a única coisa que consegue se identificar, é familiar. Ou seja, tem alguma experiência. Mesmo um iniciante sabe que um usuário deve ter atributos como nome, email, telefone, endereço, RG ou CPF e coisas assim.</p>
<p>Mas rapidamente isso complica. Estamos falando dos dados de identificação de um usuário? Ou estamos falando dos dados de autenticação, como login? Nesse caso precisamos de atributos como &ldquo;username&rdquo; e &ldquo;password&rdquo;. E já começa o primeiro erro. Já expliquei nos episódios de segurança e criptografia que não gravamos senha numa tabela. Precisamos de atributos abstratos como &ldquo;salt&rdquo; e &ldquo;hash&rdquo;. Mas e two-factor authentication? Precisamos de um seed pro algoritmo de Time-based One-Time Password ou TOTP. Só se você estudou segurança vai saber disso.</p>
<p>Dá pra complicar mais. Enviar link pra redefinir uma nova senha, caso tenha esquecido. O ideal é fazer uma tabela secundária, ou no banco de dados, ou num servidor de cache como Redis. Com um ID único que só pode ser usado uma vez, e aí o ideal é logar o endereço IP da pessoa que clicar no link, pra fazermos auditoria e ver se parece suspeito. Novamente, estratégias de segurança.</p>
<p>Se deixarmos segurança de lado. Usuário é usuário, certo? Tendo nome, email e coisas assim, tá tudo certo? Não. Digamos que o cadastro é pra parte de logística de um ecommerce. Logística precisa de um endereço. Ótimo, basta pedir o endereço. Mas cuidado, uma coisa é endereço de entrega, outra coisa é endereço de cobrança. Pra você, que é iniciante, só deve conhecer o conceito que uma pessoa só tem um endereço, certo?</p>
<p>Mas não. Uma coisa é o endereço de cobrança, pra mandar nota fiscal, outra coisa diferente é o endereço de entrega, pra onde enviar o produto. Eu posso ter endereço residencial, mas posso querer que a entrega seja feita no meu endereço comercial. Mas e se você se mudar de residência? E se você mudar de emprego? Ambos vão mudar. Ou, e se eu quiser comprar e pagar no meu cartão de crédito, mas mandar entregar como presente pros meus pais? Então preciso conseguir cadastrar endereço dos meus pais. Eu deveria criar um usuário separado pros pais? Como faz?</p>
<p>À primeira vista, endereço parecia ser só meia dúzia de campos na tabela usuário. Mas agora deve começar a ficar mais claro que pode ser uma tabela separada de endereços que precisa de uma chave-estrangeira apontando pra chave-primária do usuário. Mas não é só isso. E se meu ecommerce oferece coisas como pontos por fidelidade? Quanto mais eu comprar, mais pontos eu ganho e com isso posso usar como desconto nas compras? Pontos do usuário, ficam na tabela de usuários? Não? Então onde coloca?</p>
<p>Num sistema empresarial maior, usuários podem ter vários perfis, várias visões. Usuário pode ser funcionário da empresa, e aí vai precisar de atributos como número de PIS/PASEP pra coisas como fundo de garantia.  Usuário pode ser um fornecedor ou funcionário de um fornecedor. Fornecedor tem um cadastro separado, CNPJ, número interno de fornecedor. Usuário pode ser consumidor mesmo, daí vai ter pedidos, entregas, suporte. Usuário é um conceito muito mais complicado do que se pensa e num sistema de verdade, raramente é só uma tabela. Vai ser uma coleção de tabelas e classes. Talvez um sub-sistema inteiro separado.</p>
<p>Só pra dar outro exemplo. Falei de carro. Se você é um iniciante fazendo um ecommerce pra uma concessionária ou um concorrente de webmotors da vida, poderia pensar em fazer uma tabela chamada &ldquo;Carro&rdquo;. Daí coloca atributos como &ldquo;marca&rdquo; tipo Volks ou Fiat, modelo tipo Nivus ou Corsa. Ano, combustível, chassi e coisas assim. É o que você conhece vendo o carro dos seus pais. Mas claro, isso tá longe de completo.</p>
<p>E se quiser vender caminhonetes, ou motos, ou vãs? Melhor criar uma tabela mais genérica chamada &ldquo;Produto&rdquo; então? Os atributos vão ser mais ou menos os mesmos mas podemos colocar uma coluna de &ldquo;tipo&rdquo; pra dizer se é um carro ou moto ou outra coisa. Daí é só ter colunas como preço e já era, certo? Não tá errado, mas não é só isso nem de longe.</p>
<p>Só pra dar um pequeno exemplo: em ecommerces e varejo em geral, existe o conceito de SKUs. Stock Keeping Unit, que é um item distinto de venda, seja de produto ou serviço. É um código alfanumérico. Esse código é importantíssimo pro gerenciamento de inventário, de estoque. Faz de conta, o produto Volks Nivus, tem a versão Comfortline e Highline. A Highline tem 4 cores especiais, 2 cores metálicas, 3 cores sólidas como Preto Ninja ou Branco Cristal ou Cinza Moonstone.</p>
<p>Então o Volks Nivus Highline 2023 Cinza Moonstone tem um SKU único e diferente do Volks Nivus Highline 2023 Preto Ninja. Entende porque? Se você vai na loja, não quer saber se tem o Nivus, quer saber se tem exatamente o Highline 2023 Branco Cristal. Não adianta ter estoque do Preto Ninja, não é ele que você quer. E não adianta ter o Branco do Comfortline, você quer o Highline. O SKU reflete o exato item que você quer.</p>
<p>Mesma coisa com roupa. Ah, tem a camiseta com estampa do Iron Maiden, mas só tem o tamanho M, mas você queria o G. Camiseta Iron Maiden, tamanho M é um SKU e tamanho G é outro SKU diferente. É assim que se modela produtos numa loja. Então, numa tabela de produtos o item que você cadastra não é &ldquo;Volks Nivus&rdquo; ou &ldquo;Camiseta Iron Maiden&rdquo; e sim &ldquo;Volks Nivus Comfortline 2023 Vermelho Sunset&rdquo; ou &ldquo;Camiseta Iron Maiden tamanho PP&rdquo;.</p>
<p>Mas os problemas só começaram. Porque se você é a Volkswagen, vai ter um sub-sistema separado, a da fábrica, com outro cadastro de Volks Nivus Comfortline 2023 totalmente diferente. Pra manufatura vai ter outros atributos como o BOM ou &ldquo;Bill of Materials&rdquo;. Precisa de quais peças pra montar uma unidade? Transmissão, injeção, computador de bordo, disco de freio, etc. Cada um desses itens tem estoque, tem fornecedor, tem leilão reverso no setor de compras, tem tempo de entrega pra receber essas peças.</p>
<p>Com tudo isso temos um custo de fabricação. Isso pode, ou não, estar ligado ao sistema de vendas. Primeiro, isso influi no estoque e disponibilidade. E também influi no sistema de precificação. Preço não é um valor único na tabela de produtos, como muitos iniciantes podem pensar. É o resultado de diversos cálculos. Custo de fabricação é só um dos fatores. Tem coisas com impostos. ICMS que é o Imposto sobre Operações relativas à Circulação de Mercadorias e sobre Prestações de Serviços de Transporte Interestadual e Intermunicipal e de Comunicação.</p>
<p>Faz diferença onde o carro é fabricado, pra onde precisa ser transportado, especialmente se atravessa estados. Existem impostos federais, estaduais, municipais. Tem todo um cálculo de impostos que influi no preço final. Por isso costumava ser mais barato comprar carro em Minas Gerais do que em São Paulo, por exemplo, não sei se ainda é, mas lembro que me falaram que por isso muito carro de empresa que aluga, tem placa de Minas. O preço varia por região. Mas não é só isso, tem cálculo de custo de oportunidade, depende da estratégia da empresa. O importante é lembrar que preços não são estáticos, tem diversos fatores.</p>
<p>Beleza, temos o carro fabricado, temos o preço pro consumidor final, a venda foi feita, agora o carro precisa transitar entre a fábrica de São Bernardo Campo até a concessionária em Belo Horizonte. Pra trafegar precisa da nota fiscal. Pra colocar o caminhão na rua, precisa de rotas. De onde eu puxo informação pra rota? Google Maps? Maplink? Sistema proprietário, já que eu sou grande como uma Volks?</p>
<p>E não é só isso. Se queremos implementar sistemas de pagamentos, que lidam com dados sensitivos como números de cartão de crédito e coisas assim, nos Estados Unidos, temos o PCI DSS, Payment Card Industry Data Security Standard que dita como empresas devem proteger esse tipo de informação. E estou falando &ldquo;Estados Unidos&rdquo; daí parece que não é seu problema. Mas não se esqueça que hoje empresas são multinacionais. Pode ter empresa que começou no Brasil e agora atua internacionalmente, daí precisa implementar essas normas.</p>
<p>No caso específico de privacidade, segurança de dados de consumidores, na Europa temos o GDPR que é o General Data Protection Regulation da União Européia e no Brasil temos o LGPD, a Lei Geral de Proteção de Dados, que é similar. Enfim, o ponto é que não basta só sair modelando tabelas e jogando em qualquer lugar. Cada indústria tem seus padrões e cada país tem suas regulamentações. Precisa seguir. E pior: não é um checklist simples. Algumas coisas você é obrigado, algumas coisas dá um jeitinho, depende de como lidar com o auditor. Muitas nuances não documentadas.</p>
<p>O mundo real é muito mais complicado do que simplesmente pensar em modelagem de dados da forma acadêmica, onde nos preocupamos com coisas como normalização, se queremos terceira forma normal, ou no caso de DDD se estamos falando de Bounded Context, aggregates, domain events. Ou no caso de Design Patterns, se queremos chain-of-command ou mediator. O que eu falei até agora é parte do que chamamos das tais &ldquo;regras de negócio&rdquo; e patterns, arquiteturas, DDD da vida é a &ldquo;descrição&rdquo; da implementação dessas regras.</p>
<p>O problema é que se você não entende a regra de negócio, não interessa discutir se vai escolher um Adapter ou um Proxy, se vai organizar na terceira ou quarta forma normal. Se a regra estiver errada, a implementação vai estar errada, independente se seguiu o livro do Eric Evans ou pé da letra. E falando no livro de DDD, logo na introdução ele dá um exemplo interessante de um sisteminha de entrega de mercadorias.</p>
<p>Digamos que temos as entidades Voyage e Cargo, tipo viagem e mercadoria, onde uma viagem tem muitas mercadorias. O Eric descreve um sistema que tem responsabilidade de associar cada mercadoria com uma viagem, gravar e rastrear esse relacionamento. Até aqui tudo bem. Em algum lugar do código vamos ter um método como esse:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>public int makeBooking(Cargo cargo, Voyage voyage) {
	int confirmation = orderConfirmationSequence.next();
	voyage.addCargo(cargo, confirmation);
	return confirmation;
}</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>Mas porque existem cancelamentos de última hora, uma prática padrão da indústria de entrega é aceitar mais mercadorias do que é possível numa única viagem. É o que se chama de &ldquo;overbooking&rdquo;. Algumas vezes é usado um percentual simples, como 110% da capacidade, em outros casos tem regras mais complexas.</p>
<p>Isso é uma estratégia básica no domínio de entregas que é conhecido de qualquer empresário dessa indústria, mas normalmente nós, programadores, não sabemos disso. O requerimento que vem pra nós poderia ser algo como &ldquo;Permitir 10% de overbooking&rdquo;, o diagrama de classes poderia ficar parecido com isto e o código seria atualizado da seguinte forma:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>public int makeBooking(Cargo cargo, Voyage voyage) {
	double maxBooking = voyage.capacity() * 1.1;
	if ((voyage.bookedCargoSize() &#43; cargo.size()) &gt; maxBooking)
		return -1;
		
	int confirmation = orderConfirmationSequence.next();
	voyage.addCargo(cargo, confirmation);
	return confirmation;
}</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>Ou seja, no código determinamos quanto é o novo máximo com 10% a mais de overbooking e vemos se a mercadoria que queremos adicionar ultrapassa esse máximo. Mas isso cria um problema: agora temos uma regra de negócio escondida, como uma cláusula de guarda, no meio do método da aplicação.</p>
<p>Segundo o Eric, essa regra de overbooking é uma política. Política é outro nome pra um design pattern conhecido como Strategy. Em resumo, esse novo código poderia ser refatorado pra ter uma classe separada de OverbookingPolicy e no método de adicionar a mercadoria na viagem, chamar o método &ldquo;isAllowed&rdquo; dessa nova classes, e assim separar a política numa entidade separada, que documenta melhor essa regra em vez de deixar misturada e escondida com o código anterior.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>public int makeBooking(Cargo cargo, Voyage voyage) {
	if (!overbookingPolicy.isAllowed(cargo, voyage))
		return -1;
	int confirmation = orderConfirmationSequence.next();
	voyage.addCargo(cargo, confirmation);
	return confirmation;
}

class OverbookingPolicy {
	public static double OVERBOOKING = 1.1;
	public boolean isAllowed(Cargo cargo, Voyage voyage) {
		double maxBooking = voyage.capacity() * OVERBOOKING;
		return ((voyage.bookedCargoSize() &#43; cargo.size()) &lt;= maxBooking;
	}
}</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>Agora que eu descrevi dessa forma, deve ter ficado óbvio. &ldquo;Puts, claro, porque eu não pensei nisso antes&rdquo;. Mas o que quero expor aqui é justamente isso: pra nenhum iniciante é óbvio que isso poderia ser um strategy pattern, provavelmente não era óbvio que isso era uma &ldquo;Política&rdquo;. Todo mundo ia ter implementado do primeiro jeito e nunca ia pensar nisso. Até o dia que alguém pedisse pra mexer na regra e mudar de 10% pra 5% por exemplo. Daí iam gastar o dia inteiro tentando achar &ldquo;onde que tá definido isso no código???&rdquo;</p>
<p>Deixa eu dar outro exemplo pra ilustrar mais esse ponto. Eu gosto de quadrinhos, comics, mangá, até já me arrisquei a desenhar como hobby no passado. Todo mundo que fica empolgado com um One Piece ou Naruto da vida já rascunhou seus personagens preferidos em algum pedaço de papel e, se não for completamente iludido, sabe que saiu uma porcaria. Mas tudo bem, o objetivo era passatempo.</p>
<p>Já percebeu como todo iniciante em arte faz as mesmas coisas? Ou é paisagem de casinha com uma árvore, um sol e uma montanha. Ah sim, com uma chaminé, não pode esquecer a chaminé. Ou é close-up de rosto de anime. Agora, tenta desenhar um quarteirão de uma cidade, com ângulo olhando de cima. Ou tenta desenhar uma personagem de anime mesmo, sentada numa mesa com as pernas cruzadas e tomando um café. Vai sair a coisa mais torta do mundo, se sair.</p>
<p>Desenhar prédios pode parecer fácil, só retângulos, mas tente fazer um. Não vai conseguir. Convivemos a vida toda do lado de prédios, andamos todos os dias na frente de vários, por anos. Mas nunca paramos pra realmente olhar, observar, absorver os detalhes e, mais importante, nunca pensamos porque determinados aspectos de um prédio são tão particulares. Nunca tivemos treinamento formal de urbanismo. Não paramos pra pensar em zoneamento, por exemplo. Certas áreas não podem ter prédios comerciais, certas áreas tem limite na quantidade de andares. Quem mora perto do aeroporto de Congonhas sabe disso.</p>
<p>Por isso quando você tenta desenhar uma cidade, sai parecendo desenho infantil de casinha com sol. Pra ficar realista, precisaríamos ter experiência com urbanismo e arquitetura. Mesma coisa a personagem sentada de pernas cruzadas. A parte que mais chama a atenção de iniciantes são rostos. Por isso perde-se muito tempo só desenhando olhos, e com rostos sempre em posições parecidas, em 3/4.</p>
<p>Pra desenhar um corpo direito precisa estudar anatomia. Todo estudante de medicina no primeiro ano sabe isso, mas pra gente que não é médico, talvez nem fôssemos pensar nisso. Vemos pessoas na nossa frente todos os dias, em todas as posições. Mas quando você parou pra realmente observar uma pessoa? Como o tecido da calça interage com o material da cadeira, como o peso da perna cria dobras, como músculos como o biceps femoral e o soleus se mexem quando dobramos a perna?</p>
<p>Quando falamos de arte, imediatamente pensamos em coisas como a Capela Sistina, o magnum opus de Michelangelo Buonarroti. Contém cenas super detalhadas e difíceis como o Último Julgamento ou a Criação de Adão. É um trabalho de mais de 500 metros quadrados. Sabe quanto tempo levou pra pintar aquele monstro? Quatro anos inteiros, de 1508 a 1512, usando tecnologia do século XVI.</p>
<p>Entenda, quando Michelangelo começou o projeto da Capela Sistina ele tinha 33 anos. Mas começou a estudar arte desde muito cedo, provavelmente pelos 13 anos de idade e já como discípulo de Domenico Ghirlandaio. Depois estudou escultura sob a tutelagem de Lorenzo de&rsquo;Medici. Por anos, estudou e produziu pinturas, esculturas e já tinha entregado nada menos que a Pieta e a escultura de David antes da Capela.</p>
<p>Naquela época, a arte não era estruturada e bem dividida como hoje. A maioria dos grandes nomes como Michelangelo, chamamos de polimatas. Mas eles mesmos não se chamavam de &ldquo;polimatas&rdquo;, isso é um termo moderno. Só hoje que dividimos que anatomia é mais coisa de médico, lentes e ótica é mais coisa de fotógrafo, urbanismo é mais coisa de arquiteto, pincel é mais coisa de artista. Antigamente não existia essa divisão. Existia o problema e que conhecimentos a pessoa achava que precisava pra resolver o problema. Ponto.</p>
<p>Um artista estudava seja lá o que precisava pra melhorar sua arte. Um bom artista é crítico do seu próprio trabalho. Se ele nota que toda escultura que faz não se parece em nada com uma pessoa de verdade, que escolha ele tem? Vamos lá dissecar corpos de pessoas mortas pra investigar como a pele realmente funciona, como os órgãos influenciam o movimento e assim por diante.</p>
<p>Não existe isso que um artista só precisa estudar cores e pincel. Não existe isso que um programador só precisa estudar algoritmos e código. Um bom profissional estuda o que for necessário pra ser a melhor versão dele mesmo. Pra finalizar. Eu estava refletindo sobre animes outro dia. Já notaram como é muito comum ter várias histórias que se passam no colégio? Romance sempre é romance entre estudantes no colégio. Aventuras sempre são com adolescentes.</p>
<p>Tirando o cinismo de achar que tudo é fetiche, a resposta pode ser  mais simples do que isso. É só um chute, mas a maioria dos desenhistas de mangá começa a trabalhar logo depois do colégio, ou até abandona o colégio. E esses desenhistas fazem tudo: inventam a história e desenham. O que é a única experiência de vida que eles tiveram até esse momento? O colégio.</p>
<p>Você, iniciante que ainda não entrou na faculdade, ou mesmo recém formado, é a mesma coisa. O que você conhece sobre a vida e o mundo? Nada. Sua única experiência foi o colégio, seus únicos amigos foram do colégio, os únicos problemas que teve que resolver eram relacionados ao colégio. Festas, trabalhos, tudo. Aos 20 e poucos anos, seu conhecimento sobre o mundo é minúsculo.</p>
<p>Parece coisa de boomer dizer isso, mas é verdade: até começar a pagar seus próprios boletos em dia, você ainda é uma criança. Eu já disse isso em outros videos e vou repetir: é impossível ser um empreendedor que vai inovar o ecommerce aos 20 anos. Você nunca trabalhou num ecommerce. É impossível inovar o setor hospilar com 20 anos, você nunca trabalhou como médico. É impossível inovar o setor agrícola com 20 anos, você nunca sequer levantou uma enxada na vida. É impossível inovar qualquer setor com 20 anos, você nunca produziu nada com 20 anos.</p>
<p>E eu volto a esse ponto porque, pra programadores, temos exatamente esse problema quando discutimos modelagem de bancos de dados, modelagem de orientação a objetos, modelagem de arquiteturas. O que significa modelar? Modelar é representar alguma coisa do mundo real de uma forma abstrata. Mas eis o problema: o que você conhece do mundo real? Quase nada. Não tem como modelar alguma coisa que não se conhece.</p>
<p>Antes que alguém vá nos comentários discordar. Óbvio, eu sei que existem exceções. É por isso que se chamam exceções. Com esse disclaimer feito, vale relembrar porque estou cagando regra nesse assunto. Vamos lá, eu trabalhei em projetos de software de vários tipos de negócios diferentes. Telecomunicações, varejo, manufatura, RH, infraestrutura, agências, grandes corporações, startups. Em 30 anos passei por várias indústrias diferentes e por isso consigo comparar o que cada uma faz de diferente. E mesmo assim não posso dizer que sei tudo. Por isso aviso pra desconfiar de qualquer um que queira ensinar sem nunca ter tido experiências similares.</p>
<p>Professores de faculdade, a maioria, não tiveram essa experiência, mas eles estudaram pra profissão de ensino, fizeram pedagogia, psicologia. Eles foram treinados pra ensinar. Alguns melhores que outros, não importa, mas essa é a carreira. Porém, a grande maioria dos que criam cursos online não tem nem perto da minha experiência e nem perto do treinamento de um professor. Portanto o que tem a oferecer? Quase nada. Eles dependem de caçar material na internet, empacotar como se fossem deles e vender pra você com um discurso que parece convincente e promessas que nunca vão ser cumpridas.</p>
<p>Veja o exemplo do Michelangelo que falei. Seu primeiro mentor foi Domenico Ghirlandaio. Quem foi ele? Foi um pintor renascentista de terceira geração, especializado em frescos e fez diversos trabalhos importantes, embora não do calibre do seu aluno. Porém, ele reconheceu esse talento e foi ele quem recomendou Michelangelo pra academia de Medici. Parece ter sido um bom professor, que teve ele próprio uma boa carreira, suficiente pra catapultar seu aluno prodígio.</p>
<p>O ideal é se fosse possível todo aluno ter a possibilidade de ser mentorado por alguém experiente assim. Infelizmente isso não é possível. Não existem mentores suficientes pra todo mundo. Por isso eu digo que o correto, antes de tentar empreender qualquer coisa por conta própria, é iniciar a carreira em empresas de verdade, que tem problemas e soluções de verdade e aprender as outras áreas que não é possível ensinar numa faculdade.</p>
<p>Eu tive vários mentores que nem sabiam que estavam me mentorando. Quando eu tinha que ajudar a resolver um problema de marketing, tive a oportunidade de trabalhar com pessoas treinadas em marketing e entender o básico de como eles pensam. Quando tive que resolver problemas financeiros, tive a oportunidade de trabalhar com pessoas treinadas em contabilidade e finalmente entender coisas cabeludas como impostos. Quando tive que resolver problemas de varejo, tive a oportunidade de trabalhar com pessoas com experiência real em vendas e ver como de fato fechavam negócios. Nenhum curso ensina essas coisas.</p>
<p>O problema de todo programador é que ele assume que a parte mais difícil e mais importante é só a programação, e que todo mundo que não é programador é mais burro que ele. Por isso tem tanta dificuldade de entregar valor. Óbvio, o cliente deveria ter te explicado melhor. Basta o cara do marketing deixar de ser burro e escrever uma especificação melhor que você, o gênio, não precisa perder tempo pensando e pode só escrever seu maravilhoso código em paz.</p>
<p>Falando em ecommerces. Deixa eu dar um exemplo mais concreto. Vou falar de Ruby on Rails porque ainda é a área que eu lembro de mais referências, mas o mesmo exemplo vale pra qualquer outro framework da vida. Um dos primeiros livros que todo mundo que quer aprender Rails usa  é o Agile Web Development with Rails. Originalmente escrito pelo lendário Dave Thomas, mantido atualmente por Sam Ruby. Aliás, excelente nome pro autor de um livro de Rails.</p>
<p>Enfim, o livro é um grande tutorial pra desenvolver um mini-ecommerce à medida que explica como funciona o framework Rails. No fim, temos uma aplicação web que mais ou menos se parece com um ecommerce. Mas lembrem-se, o livro não é sobre como fazer o melhor ecommerce, só em como dá pra usar Rails pra implementar uma versão simples de um. Resolvi procurar no Github e achei o código do que é mostrado no livro, pro Rails versão 6.</p>
<p>Usando a ferramenta Tokei, podemos ver que no final do livro, seu projeto vai ter mais de 100 arquivos de Ruby, quase 2.700 linhas de código. Além de 17 arquivos de javascript que dá mais de 1.300 linhas de código. Fora outras coisas como stylesheets em Sass e um monte de arquivos JSON. Parece bastante coisa né?</p>
<p>Por acaso, no ecossistema Rails existe uma plataforma aberta de ecommerce chamada Spree Commerce. Muito boa por sinal. Eu trabalhei com ela anos atrás e até conheci o autor original Sean Schofield. Na época era um projeto monolítico, mas em anos recentes dividiram em sub-projetos. Inclusive acho que a interface web agora é feita em Next.js e o projeto principal virou mais um servidor de APIs. Vamos ver só esse repositório principal com o Tokei e olha só a diferença: temos mais de 1.300 arquivos de Ruby, mais de 92 mil linhas de código Ruby.</p>
<p>Comparado com o projetinho do livro de Rails, estamos falando de uma diferença de 34 vezes o volume de código Ruby. E isso porque não tem nenhuma lógica de front-end, só a lógica de backend mesmo. Se pegarmos só um arquivo, o da ordem de venda propriamente dita, que ambos chamam de &ldquo;Order.rb&rdquo;. O do livro é super curto, pouco mais de 50 linhas. No Spree tem mais de 700 linhas e isso porque tem um sub-diretório com outros arquivos que sub-dividem ainda mais a lógica como &ldquo;address_book.rb&rdquo; ou &ldquo;checkout.rb&rdquo; ou &ldquo;payments.rb&rdquo;. É ordens de grandeza mais complexo.</p>
<p>Agora vamos fazer uma conta de padeiro. O livro Agile Web Development with Rails tem mais de 600 páginas. Se consideramos que o produto final dele é 34 vezes menos complexo que o backend do Spree. Pra explicar o Spree do zero, o livro talvez tivesse que ter então 20 mil páginas. Tem idéia do que isso significa? Coloca em perspectiva, os 5 livros de A Song of Ice and Fire, que deu origem ao Game of Thrones, male male tem umas 4 mil páginas. Ou seja, precisaria de 5 vezes o volume de Game of Thrones, uns 20 volumes, pra descrever um ecommerce como o Spree do zero.</p>
<p>Deixa eu complicar mais: um Spree é um ecommerce muito bom, mas nem de longe é o mais completo. Um dos mais antigos e populares ecommerces de código aberto é o Magento. Hoje existe o Magento 2. Ele é feito em PHP e rodando a ferramenta tokei, olha só: são 2 MILHÕES de linhas de código PHP. Mais de 200 mil linhas de Javascript. O Magento sozinho, só no javascript, tem o dobro de linhas de código do que Ruby no Spree que, por sua vez, já era 34 vezes maior do que o ecommerce do livro que ensina Rails.</p>
<p>Se em 600 páginas o livro Agile Web Development with Rails conseguiu explicar uma versão simples de ecommerce que dá 2.700 linhas de código de Ruby, sabemos que PHP e Ruby não são equivalentes, mas só por diversão, o Magento, só em PHP é mais de 700 vezes maior. Seriam 700 livros. Seriam 100 coleções de Game of Thrones pra descrever do zero, no mínimo. Conseguem entender porque eu fico tão puto quando alguém me pergunta se tem algum livro que explica tudo? Não tem, é inviável de ter.</p>
<p>E eu vou piorar a vida de vocês. Mesmo um Magento ainda é pequeno se desse pra comparar os repositórios de código de uma Amazon da vida, com todos os seus sub-sistemas internos. O que eu estou dizendo? Entenda essa verdades: você nunca vai saber tudo. É impossível. E isso que eu descrevi é só ecommerce. Vejam meu episódio sobre SAP e seu ERP que tem meia década de idade e roda no coração de toda grande corporação no S&amp;P 500. Lembram? Só no banco de dados são dezenas de milhares de tabelas. Repetindo, dezenas de milhares de tabelas. É impossível saber tudo.</p>
<p>Se alguém ficou desesperado com esses números deixa eu contar o grande segredo: eu, Fabio Akita, nunca li o código do Spree, muito menos do Magento. Já trabalhei em projetos com eles e com outras plataformas proprietárias. Mas nunca li todo o código deles e esse é o ponto: não precisa ler tudo.</p>
<p>Posso estar enganado, mas me dá impressão que muitos iniciantes pensam que quando fazem um curso ou lêem um livro, ou um tutorial, &ldquo;como fazer seu próprio instagram&rdquo; ou &ldquo;como fazer seu próprio Facebook&rdquo;, acham que o código que sai no final já é quase a mesma coisa que o Instagram ou Facebook de verdade, faltando pouquinha coisa.</p>
<p>Ou, que o ecommerce que fez no fim do curso já é quase o Mercado Livre. Ou seja, muitos tem a noção bem errada que existe um curso ou livro que vai te ensinar 100% do que precisa saber pra fazer um produto de verdade do começo ao fim. Eu estou afirmando que não tem, não existe, e não seria prático ter. Seriam literalmente 700 livros de 600 páginas e nem isso seria tudo. Se você não consegue visualizar 700 livros, se colocados de pé um do lado do outro daria mais de 2 metros. Eu duvido que você vá ler uma fração disso na sua vida inteira.</p>
<p>Durante todos os videos do canal vim tentando explicar esse conceito. Deixa eu tentar repetir. Quando você faz faculdade de ciências da computação, em nenhum momento vai ter nenhum curso explicando como se faz um ecommerce, ou como se faz um rede social, ou como se faz um aplicativo de fintech, ou de edutech, ou de saúde, nada disso. Você aprende matérias esquisitas, como algoritmos e estruturas de dados, álgebra linear, cálculo, estatística e probabilidade. Mas onde que ensina como fazer um carrinho de compras ou lista de produtos?</p>
<p>No outro lado do espectro, você pula tudo isso e faz cursos numa Udemy da vida &ldquo;como fazer seu próprio ecommerce&rdquo; e sai com uma versão parecida com o do livro Agile Web Development with Rails que eu mostrei. Parece que é melhor, afinal, você fez um carrinho de compras e uma lista de produtos. Só que eu acabei de demonstrar que não é uma fração de projetos de código aberto como Spree ou Magento, na verdade, é de 30 a 100 vezes menor.</p>
<p>Mas eu também disse que ecommerces de verdade, como uma Amazon ou Magazine Luiza da vida, são muito diferentes do que tem num Magento. Então, como faz pra saber? Eis o ponto que eu falei sobre mentoria: você só vai saber como um Magazine Luiza funciona trabalhando dentro do Magazine Luiza. Só tem como saber como um iFood funciona, trabalhando dentro do iFood. Não tem outra forma.</p>
<p>Mas aí não é o problema do ovo e da galinha? Como eu vou conseguir trabalhar num Mercado Livre, se nenhum lugar ensina como eles funcionam? É isso que você também não está entendendo. Quando uma empresa dessas oferece emprego, nenhuma espera que você saiba como elas funcionam. Elas esperam sim, que você tenha um mínimo de ferramentas pra conseguir se adaptar ao sistema deles.</p>
<p>Por isso eu fiz videos analisando os códigos que o Twitter liberou, o código vazado do Twitch ou a arquitetura do Ingresso.com. Se não assistiu, depois assistam. Eu mesmo não trabalhei em nenhuma delas, mas é o tipo de raciocínio que é possível fazer gastando algumas poucas horas, tendo o conhecimento de base. No episódio do Ingresso.com eu falei da arquitetura de filas e salas de espera, que empresas como Amazon e outros utilizam. No episódio do Twitter explico como embeddings de álgebra linear ajudam a resolver o problema de relevância e recomendação de tweets.</p>
<p>Com a base de estatística e probabilidade, dá pra interpretar dados de monitoramento e chegar em conclusões corretas sobre comportamento dos usuários no site, baseado nos dados de navegação e compra. Com cálculo, dá pra saber como o sistema deveria se comportar, coisas com taxa e velocidade de resposta quando se aumenta o tráfego e quais estruturas de dados podem beneficiar mais certas partes do sistema. Com entendimento de sistemas operacionais e compiladores, dependendo do tamanho da empresa, dá pra trabalhar numa nova linguagem que nem existia, como já aconteceu até em empresas nacionais, como a Totus.</p>
<p>Com essa base, dá pra começar a derivar o resto do conhecimento. Uma vez dentro do sistema é que começa realmente a viver o dia a dia de operações da empresa. A burocracia fiscal, a correria de fulfillment de pedidos nos centros de distribuição, a logística de entrega, o tempo de resposta pra suporte ao cliente, as regulamentações e mudanças em leis que precisam ser refletidas no software. Com o tempo você vai aprender o que em DDD se chama de &ldquo;domínio&rdquo;.</p>
<p>O Domain, em Domain Driven Design, só faz sentido se você sabe o que compõe esse domínio. No exemplo que usei no começo do video, saber que SKU faz parte do domínio de inventário, ou saber que checkout faz parte do domínio de pagamento. E isso que eu acabei de falar não é absoluto. Em outra empresa, checkout pode fazer parte de um domínio maior chamado &ldquo;vendas&rdquo;, ou o checkout é tão complexo que pode ser um domínio por si só. Domínio, as tais &ldquo;regras de negócio&rdquo;, varia de empresa pra empresa, de projeto pra projeto. Não é uma coisa estática. É uma referência móvel.</p>
<p>Muitos iniciantes se perguntam se deveriam estudar coisas como Design Patterns e DDD logo de cara. E sim, acho que todo mundo deveria ler pelo menos uma vez, por cima, mas com o entendimento que sem experiência real, essa leitura não vai ter muita utilidade. Arquitetura, patterns, domínios, são conhecimentos que emergem naturalmente com experiência. Não é um checklist que dá pra seguir passo a passo em qualquer projeto. Estude uma vez rápido, e mantenha os livros por perto pra consultar pontos específicos, e assuma que seu entendimento inicial vai mudar na terceira vez que ler, na quarta vez que reler.</p>
<p>Se ler só uma vez ou de forma superficial, vai cometer erros que muitos iniciantes cometem. Por exemplo, achar que Bounded Contexts é só microsserviços e resolver tudo com microsserviços. Microsserviço é um estilo de arquitetura, já bounded context é um conceito de design de estratégia em DDD, que delineia as fronteiras onde um model em particular é definido e aplicável. Você pode ter bounded context num monolito simplesmente usando namespaces e módulos, por exemplo.</p>
<p>Ou então achar que DDD é basicamente só Event Sourcing e CQRS ou Command Query Responsability Segregation. Algumas partes do sistema podem realmente se beneficiar de um sistema de eventos imutáveis ou separação de escritas e leituras nos modelos. Em outros lugares vai ser overengineering. Não se aplica tudo de DDD em todos os lugares. Como diria Jack Sparrow, é mais um guideline.</p>
<p>Mesma coisa com Design Patterns ou qualquer outro livro de arquitetura. Não é pra usar tudo que se estudou em todo lugar. Saber onde usar é mais importante do que saber como usar. Uma vez que se descobre onde, o como é fácil. Muito antes de entrar nesse tipo de discussão, comece pelo básico que já falei antes: quantas vezes você já tentou baixar e rodar um Spree ou Magento ou um simples Wordpress?</p>
<p>&ldquo;Ah, mas eu sou de Python, não mexo com PHP&rdquo;. Então você é só um idiota. Porque fazer um Magento rodar não é só um problema de PHP, é subir banco de dados. Um postgres não é específico de PHP, em Python também precisa. Precisa subir um NGINX ou Apache. Numa aplicação web de Python também. Precisa saber onde e como guardar segredos como credenciais. Precisa saber como subir servidores de cache, como Memcache. Nada disso é específico de PHP. Toda aplicação web, em toda linguagem, usa os mesmos componentes. Porque você está querendo discutir patterns e arquiteturas se sequer consegue subir uma aplicação na sua própria máquina?</p>
<p>Apesar de um Magento da vida não se parecer com um Mercado Livre, os componentes e arquitetura geral é familiar. Não as linhas de código específico, mas os componentes gerais de infraestrutura, os conceitos de protocolos, redes, sistemas operacionais, configuração e tudo mais. Não é perda de tempo fuçar projetos como esses e nem mesmo as versões mais simples de tutoriais ou cursos, o meu ponto é garantir que vocês entendam que nunca vão ensinar tudo, mas como primeiro passo, tá mais que bom.</p>
<p>Outra coisa importante: um Magento tem 2 milhões de linhas de código hoje. Graças ao poder do Git, que mantém o histórico de tudo, posso facilmente voltar ao passado, pra versão 0.1.0 alpha 89, de agosto de 2014, quase 10 anos atrás, com o comando &ldquo;git checkout -b teste 0.1.0-alpha89&rdquo;. Daí podemos rodar a ferramenta Tokei de novo e olha a diferença, ele tinha mais ou menos a metade do tamanho de hoje, menos de 1 milhão de linhas de código PHP.</p>
<p>Podemos voltar pros primeiros commits, do final de 2011, começo de 2012. Não conheço toda a história do projeto. Pelo pouco que puxei no Google, parece que o Magento 1 foi criado em 2007. Uns 4 ou 5 anos depois a comunidade começou o Magento 2, por volta de 2011 a 2012. É o que os primeiros commits indicam, e a primeira versão estável só foi sair mesmo em 2015. Pelo que entendi, o Magento 2 foi um esforço homérico pra modernizar a plataforma.</p>
<p>De qualquer forma, o código inicial que serviria de base pro Magento 2 já tinha nada menos que meio milhão de linhas de código PHP. Comparado com hoje, é &ldquo;só&rdquo; um quarto do tamanho. Mas é pra dar uma noção. Levou de 2007 a 2012 pra chegar nesse meio milhão e depois quase 10 anos mais pra chegar nos 2 milhões de linhas de código de hoje. O projeto tem milhares de contribuidores. Não é um projeto que você vai varar um fim de semana e fazer igual.</p>
<p>Mas o importante é entender que todo projeto tem um dia 1 com zero linhas de código. Todo projeto de código aberto, todo grande site como uma Amazon ou Mercado Livre. No dia 1, todo projeto realmente se parece mais com o nível de código que mencionei dos tutoriais. Mas eles não ficam parados. Vão sendo testados no mundo real, feedback de usuários de verdade vão chegando, o código vai sendo moldado às necessidades do mundo real, 10 linhas se tornam 1000, que se tornam 1 milhão de linhas, ao longo de mais de uma década. E até hoje, o Magento não &ldquo;acabou&rdquo;, toda semana tem atualizações, novos commits.</p>
<p>Ao longo desse caminho, o Magento, que foi originalmente criado por uma empresa chamada Varien Inc depois virou uma entidade própria. Por volta de 2011 parece que o eBay entrou em cena e adquiriu uma parte da empresa, com objetivo de integrar com o Paypal. Mas em 2015, eBay e Paypal se tornaram duas entidades separadas e o Magento também foi separado. Daí entra a Adobe em 2018 que adquire a empresa. Hoje ela faz parte do Adobe Experience Cloud e pra contribuir no projeto você precisa assinar um acordo de Licença de Contribuidor.</p>
<p>Eis outro exemplo de como o software foi sendo adaptado pra responder a essas ações externas. Em 2007, ela não foi planejada pra fazer parte da plataforma da Adobe. O cloud da Adobe nem existia nessa época. Ninguém sabia que no meio do caminho ela iria desviar pra dentro do eBay e Paypal e nem que iria se separar poucos anos depois. O software vai se adaptando a tudo isso. A mesma coisa acontece com a plataforma de toda grande empresa. É assim que aos poucos vai virando um repositório de milhões de linhas de código. Nada disso é planejado com antecedência.</p>
<p>Modelagem é uma das respostas a esse tipo de coisa. Em algum momento, a prioridade talvez tenha sido Paypal. Em outro momento a prioridade passou a ser Adobe Cloud. Prioridades de desenvolvimento também ditam a arquitetura e arquitetura dita a modelagem. Modelagens que faziam sentido em 2011 provavelmente não fazem mais sentido em 2018. Entendam isso: software nunca está &ldquo;pronto&rdquo; ou &ldquo;finalizado&rdquo;. Se ele faz sucesso, é um trabalho constante de adaptação e evolução. Modelagem e arquitetura tem que ser respostas a essas evoluções. Elas emergem do software.</p>
<p>Todo livro, tutorial ou curso, vai simplificar isso. Não é um defeito, é uma limitação do pouco tempo. Como ilustrei até aqui, se tentar documentar tudo, precisaria fácil de centenas de volumes de livros. Ninguém ia conseguir estudar tudo isso. Nenhuma das técnicas de nenhum livro de DDD ou Design Patterns tem as respostas do que fazer em cada um desses grandes episódios. Elas servem de base pra adaptação, mas não tem nenhuma resposta pronta pra nenhum problema de negócios.</p>
<p>Do meu ponto de vista, eu diria que livros DDD ou qualquer outro de modelagem e arquitetura servem pra nos dar um vocabulário pra tentarmos nos comunicar. Tudo é abstrato em software. Não temos parâmetros físicos que todos conseguem concordar, como peso, volume, ou qualquer coisa assim. Software se chama software porque não é &ldquo;hard&quot;ware.</p>
<p>Uma simples classe de pagamento pode significar coisas extremamente diferentes pra programadores com experiências diferentes. Então tentamos racionalizar e normalizar esses conhecimentos via um vocabulário de modelagem e arquitetura pra começarmos a dar passos em busca de algum consenso, em algumas partes.</p>
<p>Pra não ficar só na filosofia, agora eu queria falar especificamente de alguns livros que acho que podem ajudar um pouco. Novamente, não são livros pra ler de cabo a rabo de uma só vez, e nem só uma vez. São livros pra se estudar um capitulo que tenha um assunto que te interessa no momento, voltar quando tiver mais experiência nesse assunto, e durante sua vida profissional ou acadêmica, quando esbarrar nos outros assuntos, vá indo um capítulo de cada vez.</p>
<p>Eu mesmo não conhecia esses livros até pouco tempo atrás, quem me recomendou foi meu sócio Talysson, que é responsável pelo programa de mentoria da minha empresa, ele mesmo um excelente programador. O primeiro livro se chama The Architecture of Open Source Applications. Ele é dividido em dois volumes. Cada capítulo é como se fosse um artigo sobre um projeto de código aberto escrito pelo próprio autor ou alguém bem experiente e reconhecido.</p>
<p>Por exemplo, o capítulo 11 do primeiro volume é sobre LLVM, que eu já expliquei em vários videos aqui do canal, como no video da Apple e GPL. E o autor desse capítulo é ninguém menos que Chris Lattner, um dos melhores engenheiros dos últimos 20 anos. LLVM tá por trás de todos os compiladores e transpilers modernos incluindo shaders de GPUs e programação de tensors pra IA.</p>
<p>O capítulo em si é super introdutório. Se você assistiu meu episódio  e outros como o de compiladores versus interpretadores, a maior parte tá lá. Ele não explica tudo, nem explica em detalhes, mas é um bom capítulo pra quem nunca parou pra ler nada sobre o assunto. A intenção é que cada autor explique porque tomou certas decisões de design do software em que trabalhou. Coisas que hoje achamos óbvio, mas que pra época não era tão óbvio assim.</p>
<p>No volume dois, capítulo 14 temos o projeto NGINX, que eu também já falei várias vezes em episódios como Tornando sua App Web mais Rápida. O autor é Andrey Alexeev, um dos co-fundadores do NGINX. E de novo, é um capítulo bem curto, onde ele fala o óbvio: porque alta concorrência é importante, ou porque não usar Apache. Hoje, é óbvio, mas 20 anos atrás não era e ele explica porque o NGINX foi feito da forma como conhecemos hoje.</p>
<p>No capítulo 18 do volume II encontrei um nome conhecido. Luke Kanies, um dos criadores do Puppet. Hoje em dia, quando falamos de infraestrutura como código, lembramos de Kubernetes, Terraform, ou Ansible, mas Puppet foi um dos precursores, quando codificar infra não era óbvio. Eu entrevistei ele pro meu blog em 2009, vou deixar o link nas descrições.</p>
<p>Já no capítulo 15 do volume 1, outro nome conhecido, Francesco Cesarini. Todo mundo da comunidade Erlang e Elixir conhece esse nome, um excelente educador e evangelista. E foi num workshop dele que eu tive meu primeiro contato com Erlang, também em 2009. Você também pode ter seu primeiro contato com ele nesse capítulo. Enfim, todos os autores desses livros são excepcionais. Eu seria feliz de ser uma fração de qualquer um deles.</p>
<p>O terceiro livro dessa série é o The Performance of Open Source Applications. É o mesmo formato, cada capítulo com um autor que é reconhecido no assunto, mas focado na parte de performance. Logo no primeiro capítulo temos Ilya Grigorik falando sobre alta performance de rede no Chrome. Ilya é outro conhecido de longa data da comunidade Ruby, que eu trouxe pra RailsConf Brasil 2009. Daí ele foi pro Google, hoje está no Shopify e ensinou nossa comunidade sobre métricas, como medir, as melhores ferramentas, quais as prioridades de otimização, e ajudou a definir muito dessa área de performance junto com a W3C. Todo desenvolvedor web deveria ler alguns dos artigos dele sobre performance.</p>
<p>O quarto e último livro é um pouco mais prático e pode servir pra vocês se exercitarem em linguagens diferentes. Se chama &ldquo;500 Lines or Less&rdquo;, cujo objetivo é desenvolver uma mini aplicação em 500 linhas ou menos. Por exemplo, no capítulo 19, Audrey Tang nos ensina a fazer uma mini planilha, tipo Excel, com Angular.js. Ou Allison Kaptur que ensina a fazer um mini interpretador de Python, em Python. Se estava procurando exercícios, vale explorar esses capítulos. De novo, nenhum deles é uma aplicação completa de verdade, só os princípios pra quem nunca parou pra pensar &ldquo;hm, como eu faria um servidor web simples do zero?&rdquo;, esse é o capítulo 22.</p>
<p>Eu não procurei mas acho que nenhum desses livros tem em português. Mas neste site, que vou deixar o link na descrição abaixo, tem tudo gratuito online. Basta abrir seu navegador e começar a explorar. Eu deixaria uma aba permanente só pra ela, pra não se esquecerem de voltar de tempos em tempos. Como disse antes, não é pra ler tudo de uma só vez. Em particular, o &ldquo;500 Lines or Less&rdquo;, é pra abrir um editor de texto e realmente tentar codificar, senão não vai servir pra nada. Alguns capítulos vão usar linguagens que talvez nunca tenham tentado, como Clojure. É uma boa desculpa pra experimentar. E mesmo se não gostar, outro bom exercício é repetir o mesmo exercício em outra linguagem.</p>
<p>Todos os livros eu diria que são adequados pro nível junior até pleno. De pleno pra senior, diria que são textos fáceis. E mesmo assim talvez tenha assuntos que vocês não tenham parado pra estudar ainda. Por exemplo, no volume 1 do The Architecture of Open Source Applications, o Russel Bryant fala do lendário Asterisk. Se nunca trabalhou em projetos de voz sobre IP, antes de existir coisas como Zoom ou Google Meet, talvez nunca tenha ouvido falar. Mas muita gente ainda usa. É tipo um PBX digital. Eu mesmo nunca mexi pessoalmente, mas trabalhei próximo de projetos que usavam, então é um pouco familiar pra mim. Só porque uma pessoa é considerada sênior numa coisa, não quer dizer que é sênior em tudo. Em Asterisk eu não sou mais que um junior.</p>
<p>O maior problema no estágio inicial é que você não sabe o que não sabe. Livros como os que recomendei e projetos de código aberto, como um Magento da vida, são ótimas portas de entrada pra molhar os pés e começar a se familiarizar com código mais complexo e mais real do que tutoriais ou cursos. Não tem nenhum problema aprender via cursos, mas não fique obcecado em achar que quantidade de tutoriais ou cursos vai determinar sua qualidade. Porque não vai. Depois de um certo ponto, sem experiência, você vai estagnar ou pior, só ter a ilusão que está aprendendo. E experiência não é só no trabalho. Você nunca vai parar de estudar e nunca vai saber tudo. Essa é a carreira.</p>
<p>Pra saber se aprendeu mesmo, precisa contribuir código em projetos. Primeiro em exercícios sozinho na sua própria máquina, depois, se possível, em projetos de código aberto mais simples, ou já direto em projetos fechados de empresas. Se puder escolher, escolha empresas que tem desenvolvedores sêniors em quem se espelhar, idealmente que podem te mentorar. Mentorar não é professor e aluno, onde você como aluno fica passivo só esperando alguém te ensinar alguma coisa. É se virar pra fazer e alguém te dar orientação e feedback. A melhor coisa é alguém apontar o que você fez de errado quando achava que não tinha nada errado. Ou seja, apontar o que você não sabia que não sabia.</p>
<p>E tire da cabeça que só tem valor conseguir emprego numa empresa de nome grande como Amazon. Não. Qualquer empresa que tenha software é oportunidade de aprendizado. Não importa se o software é &ldquo;legado&rdquo;, antigo. Você nunca programou nada real. Não importa se é na lojinha da esquina ou no Magazine Luiza. Todo software é software. Basta não ficar 20 anos no mesmo lugar, fazendo a mesma coisa. Mas também, menos de 1 ano não é proveitoso. Tem que ter um período pra aprender tudo que puder aprender, em particular, tudo que você ainda não sabe que não sabe.</p>
<p>Como disse lá no começo, esse episódio poderia ser um pouco frustrante. Porque não tem uma resposta simples pra dizer como aprender a modelar, ou decidir arquiteturas, ou fazer um bom design de software. Sua versão 1.0 de tudo vai ser sempre ruim, e isso não é um defeito, é simples falta de experiência. À medida que se trabalhar em empresas ou participar de projetos de código aberto e se exercitar mais, os gaps de conhecimento vão ficando aparentes. E você vai começar a finalmente entender o que não sabe, pra conseguir ir atrás de saber.</p>
<p>A idéia não era tentar dar uma resposta definitiva, só abrir os conceitos pros iniciantes terem uma noção melhor do que tem pela frente. E o conceito principal é que você nunca vai saber o que é um &ldquo;bom software&rdquo; sem antes fazer muito mau software e lidar com mau software dos outros. Não existe um checklist que define todas as características de um bom software. Bom é relativo ao que se espera daquele software. E tudo que chamamos de modelagem ou design, são meros vocabulários pra tentar descrever essa abstração que chamamos de software. Se ficaram com dúvidas, mandem nos comentários abaixo. Se curtiram o video deixem um joinha. Assinem o canal e não deixem de compartilhar o video com seus amigos. A gente se vê, até mais.</p>
]]></content:encoded><category>arquitetura-de-software</category><category>engenharia-de-software</category><category>aprendizado</category><category>akitando</category></item><item><title>[Akitando] #113 - A Forma Ideal de Projetos Web | Os 12 Fatores</title><link>https://akitaonrails.github.io/2022/01/29/akitando-113-a-forma-ideal-de-projetos-web-os-12-fatores/</link><guid isPermaLink="true">https://akitaonrails.github.io/2022/01/29/akitando-113-a-forma-ideal-de-projetos-web-os-12-fatores/</guid><pubDate>Sat, 29 Jan 2022 16:59:00 -0300</pubDate><description>&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/gpJgtED36U4"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2&gt;DESCRIPTION&lt;span class="hx:absolute hx:-mt-20" id="description"&gt;&lt;/span&gt;
&lt;a href="#description" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Finalmente vou mostrar o que é o Heroku e como é o fluxo de trabalho mínimo de um projeto web ideal. Se você já usa Heroku, aproveite pra compartilhar o video com conhecidos que ainda não usam. Se nunca viu Heroku, prepare-se pra ficar surpreso!&lt;/p&gt;
&lt;h2&gt;Conteúdo&lt;span class="hx:absolute hx:-mt-20" id="conteúdo"&gt;&lt;/span&gt;
&lt;a href="#conte%c3%bado" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;00:00 - Intro&lt;/li&gt;
&lt;li&gt;01:16 - Recapitulando Heroku&lt;/li&gt;
&lt;li&gt;05:04 - Os 12 Fatores&lt;/li&gt;
&lt;li&gt;06:02 - I - Codebase&lt;/li&gt;
&lt;li&gt;07:11 - II - Dependencies&lt;/li&gt;
&lt;li&gt;08:24 - III - Config&lt;/li&gt;
&lt;li&gt;09:30 - IV - Backing Services&lt;/li&gt;
&lt;li&gt;10:47 - V - Build, release, run&lt;/li&gt;
&lt;li&gt;12:25 - VI - Processes&lt;/li&gt;
&lt;li&gt;14:50 - VII - Port Binding&lt;/li&gt;
&lt;li&gt;16:09 - VIII - Concurrency&lt;/li&gt;
&lt;li&gt;17:40 - IX - Disposability&lt;/li&gt;
&lt;li&gt;19:07 - X - Dev/Prod parity&lt;/li&gt;
&lt;li&gt;21:59 - XI - Logs&lt;/li&gt;
&lt;li&gt;22:31 - RANT: log4j&lt;/li&gt;
&lt;li&gt;23:39 - XII - Admin Processes&lt;/li&gt;
&lt;li&gt;24:33 - Heroku - alternativas e custo-benefício&lt;/li&gt;
&lt;li&gt;28:37 - Escalabilidade não é automática&lt;/li&gt;
&lt;li&gt;30:33 - 1. Farás Testes!&lt;/li&gt;
&lt;li&gt;31:05 - 2. Integrarás Continuamente!&lt;/li&gt;
&lt;li&gt;31:59 - 3. Usarás um Sistema de CI!&lt;/li&gt;
&lt;li&gt;32:44 - 4. Farás Deploy Contínuo!&lt;/li&gt;
&lt;li&gt;33:16 - Conclusão: equipes saudáveis&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Links&lt;span class="hx:absolute hx:-mt-20" id="links"&gt;&lt;/span&gt;
&lt;a href="#links" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;The Twelve-Factor App (&lt;a href="https://12factor.net/"target="_blank" rel="noopener"&gt;https://12factor.net/&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Guidance for preventing, detecting, and hunting for exploitation of the Log4j 2 vulnerability (&lt;a href="https://www.microsoft.com/security/blog/2021/12/11/guidance-for-preventing-detecting-and-hunting-for-cve-2021-44228-log4j-2-exploitation/"target="_blank" rel="noopener"&gt;https://www.microsoft.com/security/blog/2021/12/11/guidance-for-preventing-detecting-and-hunting-for-cve-2021-44228-log4j-2-exploitation/&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;SCRIPT&lt;span class="hx:absolute hx:-mt-20" id="script"&gt;&lt;/span&gt;
&lt;a href="#script" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Olá pessoal, Fabio Akita.&lt;/p&gt;</description><content:encoded><![CDATA[

<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/gpJgtED36U4"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<h2>DESCRIPTION<span class="hx:absolute hx:-mt-20" id="description"></span>
    <a href="#description" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Finalmente vou mostrar o que é o Heroku e como é o fluxo de trabalho mínimo de um projeto web ideal. Se você já usa Heroku, aproveite pra compartilhar o video com conhecidos que ainda não usam. Se nunca viu Heroku, prepare-se pra ficar surpreso!</p>
<h2>Conteúdo<span class="hx:absolute hx:-mt-20" id="conteúdo"></span>
    <a href="#conte%c3%bado" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li>00:00 - Intro</li>
<li>01:16 - Recapitulando Heroku</li>
<li>05:04 - Os 12 Fatores</li>
<li>06:02 - I - Codebase</li>
<li>07:11 - II - Dependencies</li>
<li>08:24 - III - Config</li>
<li>09:30 - IV - Backing Services</li>
<li>10:47 - V - Build, release, run</li>
<li>12:25 - VI - Processes</li>
<li>14:50 - VII - Port Binding</li>
<li>16:09 - VIII - Concurrency</li>
<li>17:40 - IX - Disposability</li>
<li>19:07 - X - Dev/Prod parity</li>
<li>21:59 - XI - Logs</li>
<li>22:31 - RANT: log4j</li>
<li>23:39 - XII - Admin Processes</li>
<li>24:33 - Heroku - alternativas e custo-benefício</li>
<li>28:37 - Escalabilidade não é automática</li>
<li>30:33 - 1. Farás Testes!</li>
<li>31:05 - 2. Integrarás Continuamente!</li>
<li>31:59 - 3. Usarás um Sistema de CI!</li>
<li>32:44 - 4. Farás Deploy Contínuo!</li>
<li>33:16 - Conclusão: equipes saudáveis</li>
</ul>
<h2>Links<span class="hx:absolute hx:-mt-20" id="links"></span>
    <a href="#links" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li>The Twelve-Factor App (<a href="https://12factor.net/"target="_blank" rel="noopener">https://12factor.net/</a>)</li>
<li>Guidance for preventing, detecting, and hunting for exploitation of the Log4j 2 vulnerability  (<a href="https://www.microsoft.com/security/blog/2021/12/11/guidance-for-preventing-detecting-and-hunting-for-cve-2021-44228-log4j-2-exploitation/"target="_blank" rel="noopener">https://www.microsoft.com/security/blog/2021/12/11/guidance-for-preventing-detecting-and-hunting-for-cve-2021-44228-log4j-2-exploitation/</a>)</li>
</ul>
<h2>SCRIPT<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita.</p>
<p>No episódio anterior me dei ao trabalho de fazer o passo a passo de um tutorial simples de primeiros passos usando o Heroku pra fazer deploy de um projetinho de exemplo em PHP. O objetivo foi mostrar o processo de deployment de uma aplicação moderna pra iniciantes que talvez nunca nem tenham ouvido falar de Heroku. Qualquer desenvolvedor que já trabalha com certeza já conhecia. Então, se você não sabe o que é Heroku assista o episódio anterior antes de ver este, porque precisa saber o que eu disse lá pra entender este.</p>
<p>Hoje quero falar sobre um troço conhecido como os “12 fatores” que é meio uma metodologia que pode ser aplicada a projetos web escritos em qualquer linguagem de programação e qualquer framework web. Além disso quero complementar com alguns fatores que originalmente não se menciona mas meio que ficam implícitos. Se você quer um projeto web que tenha capacidade de escalar é meio obrigatório que no mínimo esses 12 fatores estejam satisfeitos. Satisfazer os 12 fatores não torna nenhuma aplicação automaticamente escalável, mas não satisfazer eles quase garante que não vai escalar de jeito nenhum.</p>
<p>(&hellip;)</p>
<p>Se você já é programador de sistemas legados, tenho certeza que quando viu o tutorial de Heroku pela primeira vez, brilhou seus olhos de vontade de poder trabalhar desse jeito. Se você é de agenciazinhas porcaria - e eu defino como porcaria todas que ainda atualizam sites via FTP como falei antes - mesma coisa, deve estar se coçando de vontade de poder trabalhar assim. Todo mundo que trabalha em tech startups hoje em dia ou empresas grandes que se modernizaram já trabalham desta forma. Mas cuidado, se você não trabalha assim e mesmo assistindo até aqui achou tudo desnecessário ou mesmo ruim, eu repensaria a carreira. Você vai se tornar obsoleto muito em breve.</p>
<p>E quem acha que isso tudo é só mais uma novidade passageira, que vai acabar daqui a pouco, está bem enganado. Eu trabalho desse jeito desde aproximadamente 2010, portanto mais de uma década. Eu ajudei o quanto pude a difundir esse estilo de trabalho por anos e finalmente isso se tornou o padrão. Muitos tentaram imitar o Heroku, mas até hoje acho impressionante que poucos conseguiram chegar perto.</p>
<p>Só resumindo se você não lembra do episódio passado, o grande lance é você não configurar um servidor manualmente, ser tudo automatizado. E principalmente não instalar todos os componentes da aplicação no mesmo lugar, deixar cada coisa num container separado, como serviços externos independentes. Além disso conseguir fazer deploys frequentes a cada nova funcionalidade ou correção de bug. Tudo tão fácil quanto fazer um <code>git push</code>, literalmente.</p>
<p>Na mesma época que o Heroku surgiu, o Google tinha acabado de lançar um produto que fez muito barulho, o Google App Engine pra rodar aplicações feita em Python usando serviços do Google. O problema é que você não podia usar componentes padrão de Linux como banco de dados Postgres. Precisava usar o banco do Google que era o BigTable, um NoSQL que só existe no Google e não dá pra rodar em outro lugar. Além disso precisava programar de formas específicas pro App Engine. Então, uma vez que funcionasse não dava pra rodar em nenhum outro lugar. Era super simples de subir a aplicação mas era super limitado, restritivo e você ficava preso.</p>
<p>A grande sacada do Heroku foi abrir os padrões e ajudar a criar uma forma onde ao mesmo tempo você queria ficar no Heroku porque tudo era muito simples e seguro, mas não precisava lobotomizar seu código pra fazer rodar. Se quisesse sair e instalar sua própria infraestrutura, sua aplicação não tinha nada proprietário te prendendo. Além disso, eles criaram o conceito de marketplace de software as a service pra desenvolvedores. Você podia sair do Heroku, instalar sua aplicação manualmente num VPS do Linode e ainda usar o mesmo serviço de Papertrail que mostrei antes lá, por exemplo. Os addons também são independentes do Heroku.</p>
<p>Mas se ainda não ficou óbvio, você não escolhe infraestrutura querendo que seja barata demais, porque você recebe exatamente aquilo que paga. Se paga barato, não espere ter garantia de nada, aliás, espere que seu sistema vai cair quando mais precisa dele e vai sair super caro colocar tudo no ar de novo pra aguentar a carga. É por isso que sempre que possível, a recomendação é começar no Heroku. No outro extremo, se tem dinheiro sobrando, não recomendo ir direto pra um Kubernetes, começa no Heroku. Só faz infra customizada quando tem dados de uso que realmente justificam isso.</p>
<p>O Heroku mantém as coisas atualizadas. Patches de segurança e tudo mais são aplicados na sua infra, e na maioria dos casos você não precisa fazer nada. O jeito como o Heroku te obriga a organizar seu projeto, não insere nada de proprietário, mas ao mesmo tempo te força a fazer as coisas do jeito certo é a principal vantagem. Quando alguma coisa não funciona no Heroku normalmente não é um problema do Heroku, é problema da sua aplicação que foi feita de um jeito porco.</p>
<p>E pra facilitar a educação do jeito certo de programar aplicações web, o co-fundador Adam Wiggins escreveu um site chamado The 12 Factors que lista os 12 fatores que todo projeto web deve ter pra ser minimamente possível instalar numa infraestrutura escalável como o Heroku. É como se fossem 12 patterns de gerenciamento de arquitetura de projetos. Esse site foi publicado acho que em 2011 e tudo que está nele continua válido igualzinho até hoje.</p>
<p>Quem acompanha este canal e conseguiu acompanhar o tutorial de Heroku até aqui já entende a maioria, mas quero comentar ponto a ponto desses doze fatores. Se você é iniciante, preste muita atenção. É isso que se espera que todo programador web saiba de cor e salteado, independente da linguagem ou framework que escolher. Aliás, todos os frameworks web modernos de hoje facilitam você implementar esses 12 fatores. Um framework que não é compatível com esses fatores é um péssimo framework e você não deveria estar usando.</p>
<p>Vamos lá, o ponto 1 declara que todo código-fonte deve estar num repositório de versionamento de código. Hoje em dia isso só tem uma resposta e se chama Git. Tudo pode estar num único repositório Git, se for um projeto gigantesco talvez esteja dividido em múltiplos repositórios ou monorepos. Não importa, contanto que esteja em Git que todo mundo da equipe tenha acesso e que se faça deploys com frequência. Esse ponto é chave.</p>
<p>No próprio tutorial do episódio passado eu fiz pelo menos uma meia dúzia de deploys enquanto ia adicionando novas funcionalidades. Antigamente fazer deploys era um processo demorado, manual, cheio de erros humanos. Hoje em dia o certo é que se faça deploys contínuos. Múltiplos por semana, múltiplos por dia. No mínimo em ambiente que chamamos de staging, que é tipo um ambiente de testes onde tudo roda como se fosse ambiente de produção. Não pode existir uma dúzia de desenvolvedores fazendo código semanas inteiras e levar mais semanas pra subir esse código numa infraestrutura de testes. É inadmissível e é sinal claro de incompetência da gerência dessa equipe.</p>
<p>O segundo fator se chama Dependências e foi por isso que me alonguei tanto falando de gerenciamento de dependências durante o tutorial. É fator fundamental que todas as dependências de um projeto web não dependam de pacotes específicos de sistema operacional instalado na máquina. Isso porque o sistema operacional dos containers do servidor e o sistema na máquina de cada desenvolvedor sempre vão ser diferentes. E versões diferentes de cada pacotes vão estar sendo usados em cada lugar, aí a gente nunca sabe de onde está vindo muitos bugs, se é problema no código ou se é dependência com versões erradas.</p>
<p>Repetindo: toda dependência deve estar declarada num arquivo de manifesto como <code>composer.json</code> no nosso projeto de exemplo, ou <code>package.json</code> num projeto Node. E tanto na máquina do desenvolvedor quanto no script de deploy como do Heroku, vamos rodar o mesmo comando pra baixar as dependências, como o <code>compose update</code> ou <code>npm install</code> da vida. E em todos os lugares vamos garantidamente ter exatamente as mesmas dependências nas exatas versões necessárias. Isso é essencial e novamente, projetos que não estão assim demonstram incapacidade da gerência do projeto.</p>
<p>O terceiro fator se chama Configuração. Em resumo, foi o que expliquei no tutorial sobre a diferença de configuração na máquina de desenvolvimento usando recursos do framework que carrega arquivos como <code>.env</code> ou algum arquivo JSON da vida e variáveis de ambiente. Todo framework competente permite executar a aplicação em ambientes distintos pra desenvolvimento, testes e produção, no mínimo. Isso porque em desenvolvimento queremos mais detalhes de erros, mais bibliotecas de debugging e monitoramento, mas em produção não queremos divulgar nenhum detalhe de erros e nem desperdiçar memória carregando bibliotecas que só servem em desenvolvimento.</p>
<p>Em cada um desses ambientes precisamos de variáveis de configuração diferentes, pro ambiente de testes precisamos conectar num banco de dados de teste, por exemplo. Ou não mandar SMS ou notificações de verdade e só logar que tentou mandar. Tudo isso precisa estar facilmente configurável em variáveis de ambiente. Leia os detalhes sobre configuração do seu framework, tem muitas nuances que você precisa estudar e testar com calma.</p>
<p>O fator 4 é mais relevante se você já é um programador das antigas que instalava tudo num servidor como já expliquei inúmeras vezes que não deveria, com stacks como LAMP. Pra você havia diferença num banco de dados que roda no mesmo localhost da sua aplicação web e um serviço externo de enviar e-mails, por exemplo. Numa aplicação 12 fatores não existe essa distinção. Tudo é um serviço externo, ou um recurso externo, acessível via uma URL. Nada roda junto do container da sua aplicação web, tudo roda externamente. Esse é o quarto fator, backing services.</p>
<p>É extritamente proibido instalar um servidor MySQL junto com um Redis ou um sistema de filas como RabbitMQ. No mínimo do mínimo, se tiver um hardware parrudo, cada serviço deve estar rodando isolado no seu próprio container ou máquina virtual. Mesmo na sua máquina de desenvolvimento, se possível não instale tudo junto. Rode cada coisa num container Docker e orquestre tudo com Docker Compose. Estude sobre Docker Compose e experimente transformar seu projeto tradicional num conjunto de containers. É até uma boa forma de garantir que você não largou conexões hardcoded pra localhost, que vai funcionar na sua máquina de desenvolvimento mas vai falhar em produção.</p>
<p>O fator 5 é mais específico pra quem mexe com infra, mas é sobre separar a fase de build da fase de execução. Lembra como toda vez que fazemos <code>git push heroku</code> eu falei que o Heroku criava uma nova imagem, semelhante ao que o Docker faz quando executamos <code>docker build</code>? É isso que quero dizer. Antigamente a gente faria o equivalente build manualmente copiando arquivos via FTP e logo na sequência já executaríamos a aplicação e deixaríamos lá rodando.</p>
<p>Pior, se tivesse algum bugzinho, o normal era abrir um SSH pro servidor ou, Deus me livre, via Telnet, editar algum arquivo manualmente direto lá e pronto. Era super fácil e também a raíz de milhares de problemas. Primeiro porque tudo que você editou na mão direto no servidor não está no repositório de código. Se der pau no servidor, você perde a modificação e tudo pára de funcionar e ninguém sabe porque. Voltamos ao fator 1 que declara que 100% de tudo deve sempre estar num repositório Git.</p>
<p>Segundo porque digamos que queremos voltar uma release, como expliquei que o Heroku faz. Não tem como, porque todo novo deploy sobrescreve em cima da versão antiga de maneira permanente. Mas gerando builds, sempre vamos ter uma cópia da imagem antiga e podemos facilmente voltar pra ela. E como cada imagem não depende de pacotes específicos do sistema operacional, tudo que ela precisa pra rodar está na imagem. Portanto trocar imagens é uma operação trivial que pode ser feita dezenas de vezes e vai funcionar em todas as vezes, sem depender de nenhuma pessoa entrando nos servidores e subindo coisas manualmente.</p>
<p>O fator 6 provavelmente é um dos mais difíceis de entender se você for um iniciante. Ela se chama Processos mas na verdade é o fator que define que sua aplicação deve ser share-nothing, ou mais corretamente, stateless, sem estado. O exemplo mais simples é que sua aplicação nunca deve gravar nada no sistema de arquivos. Por exemplo, digamos que você faça uma página pra fazer upload de fotos pra colocar na página de perfil de cada usuário.</p>
<p>Você segue um tutorial qualquer que manda ir gravando tudo num diretório chamado uploads no servidor. E tudo vai funcionar perfeitamente. Até você fazer um novo deploy no Heroku ou outro sistema de containers e aí vai descobrir que todas as suas fotos sumiram. Os únicos arquivos que existem na sua aplicação são aqueles que existiam na fase de build da imagem, que é o código fonte que baixou do Git, as bibliotecas que um gerenciador de dependências como o Composer baixou, e outros arquivos gerados por scripts executados no build, como arquivos de javascript compilados por um Webpack.</p>
<p>Uma vez que a build foi fechada, ela não pode mais ser modificada, é pra ser considerada read-only. Daí vocês vão lembrar que eu falei durante o tutorial que o Heroku vai derrubar os dynos que estavam executando a versão antiga e subir novos dynos com a imagem nova. Entenderam? Quando o container é desligado, tudo que estava nele é perdido, por exemplo as fotos no diretório uploads. Quando um novo dyno subir, os únicos arquivos que vão ser carregados são os que estão na imagem que acabamos de construir.</p>
<p>No caso de um Docker na sua máquina local é a mesma coisa, mas você tem a opção de mapear um diretório externo fora do container e remontar esse diretório quando subir o container de novo, daí os arquivos ainda persistem, mas sempre fora do container. Tudo dentro de um container deve ser considerado efêmero.</p>
<p>Todo dado que você quer que sobreviva a um deploy deve obrigatoriamente estar num armazenamento externo, um backing service como descrito no fator 4. Por exemplo num banco de dados como Postgres ou Redis e, no caso de arquivos, num serviço como o AWS S3 ou Azure Blob ou Google Cloud Storage da vida. Tem várias outras opções. Mesma coisa logs, por isso o certo é adicionar addons como um Papertrail. Qualquer log que for gravado como arquivo dentro do container vai ser perdido no próximo deploy, e esse é o jeito certo de se fazer aplicações web. Vamos falar mais sobre log no final.</p>
<p>O fator 7 não é tanto uma preocupação pra nós desenvolvedores, especialmente hoje em dia. Ele só declara que todo serviço que precisamos deve estar exposto numa porta de rede. Como nossa aplicação web que costuma estar exposta em portas como 3000 ou 8080, ou MySQL que costuma estar na porta 3601 e assim por diante.</p>
<p>Isso eu acho que é mais pra dizer que não devemos usar serviços que não estejam expostos em portas. Por exemplo, pense no serviço cron que existe em todo Linux e serve pra schedular tarefas. A gente configura num arquivo pra executar algum comando em alguma determinada hora do dia ou periodicamente, tipo todo dia às 4 da manhã se for um backup. O único ponto de contato com esse serviço é via um arquivo de configuração, por isso não podemos usar cron numa infraestrutura dessas, com vários containers que não permite modificar arquivos localmente.</p>
<p>Em vez disso vamos usar um software as a service, um SaaS, que adiciona uma aplicação web na frente do cron, ele vai ser instalado como um backing service, um serviço externo e nossa aplicação web vai interagir com esse serviço via HTTP, conectando na porta web dele. Assim, a requisição pode sair de qualquer um dos nossos containers web e todos vão consistentemente chegar no mesmo serviço sempre, assim como já chegam em outros serviços como o banco de dados.</p>
<p>O fator 8 se chama Concorrência e meio que só deriva dos fatores anteriores. Ele declara que a forma de escalar é horizontal. Ou seja, como nossa aplicação é share-nothing, ou seja, não depende de nenhum estado específico da máquina onde está, e como já é tudo organizado como builds que podemos subir como containers em qualquer máquina, podemos rapidamente aumentar ou diminuir a quantidade de containers horizontalmente.</p>
<p>Esse fator é inspirado no jeito UNIX de pensar onde tudo são processos e a forma de escalar é dar fork em mais processos. Mesmo quando usamos alguma linguagem como Javascript ou Elixir, que permite fazer multiplexing via threads ou I/O assíncrono dentro de cada processo, ainda assim sempre vai estar limitada pelo tamanho da máquina onde está rodando e quando esse recurso se exaurir, vai precisa escalar horizontalmente de qualquer jeito. Escalabilidade exige expansão e contração horizontal, que é mexer na quantidade de containers e/ou na quantidade de máquinas físicas.</p>
<p>Particularmente um Heroku facilita isso porque quando você escala horizontalmente containers web, vai precisar de um load balancer na frente pra distribuir a carga de requisições. É algo que se você só subiu aplicações pequenas nunca teve que pensar, porque só tinha um servidor web e todas as requisições iam direto pra ele. Com mais um servidor web, você precisa de um balanceador de carga como NGINX ou HAProxy na frente. Um Heroku da vida já te dá esse balanceador por padrão de forma transparente.</p>
<p>O fator 9 é um pouco mais avançado de entender e eu recomendo estudar sobre sinais de processos como SIGTERM e o que é shutdown gracioso e conceitos como rolling restart. Mas na prática esse fator fala sobre Descartabilidade. Você já viu isso na prática no tutorial toda vez que fizemos <code>git push heroku</code>. Ele faz a build de uma nova imagem e descarta os containers antigos pra subir novos. É isso que significa ser descartável. Uma vez que garantimos que não dependemos de nada dentro do container, todos os dados estão seguros num banco de dados ou outros serviços como o AWS S3 pra arquivos remotos, podemos facilmente descartar containers pra fazer deploy de uma nova release ou dar rollback pra releases anteriores.</p>
<p>Antigamente, quando a gente instalava tudo num mesmo servidor, não dava pra fazer isso. E se a máquina era invadida por um hacker? É impossível recuperar uma máquina comprometida, o certo é jogar tudo fora e remontar do zero. Mas se você editava coisas via SSH direto no servidor, se parte do que rodava não estava no Git, e se sua aplicação gravava uploads e logs localmente como arquivos. E agora? Como você vai conseguir reinstalar essa máquina o mais rápido possível? E a resposta é simples: não vai. Um sistema robusto precisa ser descartável. Você precisa conseguir apagar o servidor inteiro e reinstalar tudo automaticamente via script sem perder 1 bit de dados e em poucos segundos.</p>
<p>O ponto 10 é pra reforçar o que acabei de falar, que é manter ambientes de desenvolvimento, staging e produção o mais similares quanto possível. E isso obviamente significa tudo em repositório Git, significa builds automatizados e significa jamais editar arquivos direto no servidor. Os benefícios de fazer as coisas dessa forma é que gerenciar infraestrutura se torna muito mais fácil e quase trivial em alguns casos. É assim que nasce a área de devops de verdade.</p>
<p>Mais do que isso, esse fator significa fazer deploys pra staging e produção o mais rápido quanto possível pra estar próximo do que está rodando na máquina dos desenvolvedores. Nada disso de demorar um mês ou mais pra fechar uma release e mandar pra produção. Estude sobre Continuous Deployment. Plataformas como GitHub e GitLab tem configurações pra criar builds e rodar os testes da sua aplicação a cada novo commit que aparece. E se tudo passar eles fazem automaticamente o <code>git push heroku</code> pra staging ou pra produção.</p>
<p>Parece perigoso isso de subir pros servidores automaticamente mas só é perigoso se sua gerência é uma droga. A quantidade de bugs em produção reflete inversamente a qualidade de comunicação da sua equipe. Uma equipe disfuncional vai subir porcaria pra produção não importa quantos mecanismos de gerenciamento você coloque em cima deles.</p>
<p>Uma equipe saudável revisa o código um do outro antes de mergear um pull request na branch <code>main</code> do Git. Uma equipe saudável sempre adiciona testes pra cada nova funcionalidade e sempre adiciona testes que simulam um bug recém reportado, dessa forma esse bug não vai aparecer de novo no futuro. Uma equipe saudável implementa novas funcionalidades atrás um &ldquo;feature flag&rdquo;. Seja via variável de ambiente ou via permissão pra determinados usuários testarem e pros demais usuários não conseguirem enxergar essa nova funcionalidade até toda equipe ter chance de testar e aprovar. Ou seja, mesmo a desculpa que uma nova funcionalidade vai levar semanas pra ficar pronto não é justificativa suficiente pra não estar integrando o código no repositório com frequência e não estar fazendo deploys com frequência. Quanto mais se demora pra integrar e deployar mais rápido aumenta a quantidade de bugs e conflitos, é inevitável.</p>
<p>Existem diversas estratégias de comunicação e organização que uma equipe saudável pode aplicar e eu acabei de descrever algumas. Estude sobre Testes, Integração Contínua e Deploy Contínuo. Só equipes disfuncionais e/ou com péssima gerência são incapazes de trabalhar de forma integrada e contínua. Lembrem-se, não importa a linguagem ou framework, produtividade, bugs, tudo isso é reflexo da qualidade de comunicação de uma equipe. Sempre.</p>
<p>O fator 11 de novo é meio específico pra quem desenvolve frameworks, mas fala sobre logs serem streams em vez de arquivos. Todo bom sistema manda logs pro STDOUT. Se por acaso você começou a aprender sobre logs, rotação de logs e coisas assim, esquece. Num container todo serviço deve escrever logs pro STDOUT, sempre. Daí a plataforma de infraestrutura, como um Heroku, vai capturar esses streams e mandar pra algum serviço externo de logs, como o Papertrail que mostrei antes.</p>
<p>Fica a dica que um sistema de log que tenta fazer coisas demais uma hora vai dar catástrofe. Que foi o que aconteceu com o caso recente do Log4j. É bem coisa de javeiro enterprise fazer uma mísera biblioteca de Log que, por alguma razão do além, permite executar comandos que vem no log e ainda vem junto com bibliotecas como de lookup de JNDI que se conecta com o mundo exterior. É o cúmulo da estupidez, pra dizer o mínimo.</p>
<p>Um log4j nem precisa muito existir, pelo menos não do jeito como é hoje. Todo log deveria ir pro STDOUT e um serviço externo, separado, como Logstash, Kibana e coisas assim que deveriam consumir esses logs e organizar. Isso é fruto daquela mentalidade de arquiteto chimfrin que pensa, &ldquo;ah, e se amanhã eu precisar dessa opção?&rdquo; E isso tá errado. Nunca faça coisas que não precisa pra hoje. O dia que a necessidade aparecer aí pensa qual a melhor forma de resolver, mas não faça nada porque tem chances de amanhã precisar. Isso é software desnecessário, e todo software desnecessário é um buraco de segurança esperando pra ser descoberto.</p>
<p>Finalmente o fator 12, que fala de processos de administração, que eu já mostrei no tutorial pra vocês. A idéia é que você nunca abre um SSH direto pra um dos containers web. Toda tarefa administrativa fora do comum deve ser executado num container isolado e separado dos demais, sem concorrer os mesmos recursos com as requisições de usuários.</p>
<p>Lembram quando rodei <code>heroku run bash</code> que abriu um container direto no bash? Ou quando rodei <code>heroku pg:psql</code> que abriu o console do Postgres pra criar a tabela que faltava? Em ambos os casos o Heroku subiu um novo container com a imagem da última release, ou seja, idêntico ao conteúdo dos containers web, mas fora do load balancer. Assim eu rodo no mesmo ambiente e quando terminar e desconectar, esse container é automaticamente destruído. E isso é importante, lembra o fator sobre descartabilidade?</p>
<p>Os 12 fatores foram muito influentes quando foram lançados porque na época o único framework que implementava com sucesso todos os fatores era o Ruby on Rails. Levou um tempão pros outros alcançarem esse mínimo. Produtos open source que não usavam nenhum framework como Wordpress ou Magento não implementavam todos os 12 fatores por padrão e também levou um tempo pra se adequarem.</p>
<p>O Heroku foi lançado entre 2008 e 2009, só uns 2 anos depois que a Amazon AWS apareceu. Naquela época a maioria de nós só tinha mentalidade de servidor virtual, no máximo, VPS. Ninguém pensava em particionar recursos da mesma máquina em containers e muito menos precificar por containers. Docker ainda estava a anos de ser lançado e na verdade muito do Docker foi influenciado pelo Heroku.</p>
<p>A Amazon estava muito a frente do seu tempo, tanto que ninguém sabia exatamente qual a melhor forma de tirar proveito dessa idéia de infraestrutura elástica. O que significava ser elástico? Como lidar com máquinas voláteis e descartáveis que quando reiniciam apagam tudo que tinha dentro? O Heroku foi a primeira plataforma como serviço ou PaaS que de fato soube aproveitar a infraestrutura que a AWS estava oferecendo.</p>
<p>O sonho de muitas empresas é ter um Heroku privado, o famoso private cloud, onde equipes internas poderiam facilmente dar <code>git push</code> e o deploy aconteceria automaticamente. E muitas empresas já trabalham assim. É o que todo mundo que tenta instalar Kubernetes busca. O que o Heroku chama de dynos, o Kubernetes chama de pods. A linha de comando <code>kubectl</code> da vida é inspirado na linha de comando do Heroku. Enfim, se você lida com devops de alguma forma hoje em dia, tenha certeza que muita coisa foi inspirado no Heroku.</p>
<p>Se parece fanboy falando, pode dizer que sou mesmo, porque em mais de 10 anos entregando projetos de verdade pra clientes, ainda não vi outra solução que chega perto em termos de simplicidade, flexibilidade, organização e custo benefício. Muitos acham caro a faixa de 30 dólares por dyno e é mesmo, e fazem a conta comparando com o preço bruto da hora de uma máquina no EC2. Mas a conta tá errada. A conta certa é calcular quanto custa ter uma pessoa experiente de devops de plantão todo dia pra manter sua infra. O cálculo certo não é da máquina mas do cara de devops que você não precisa ter quando usa o Heroku.</p>
<p>Aliás, antes que alguém vá nos comentários dizer sim, eu sei que existem soluções como o Dokku que implementa um Heroku light que você mesmo pode instalar num VPS da vida. Você também pode usar direto Docker Machine pra orquestar suas VPS. Mas de novo, você precisa adicionar o custo de alguém de infra pra dar manutenção nessa infraestrutura. Nunca vai ser plug and play que uma vez instalado ninguém mais precisa mexer. Não caia nessa armadilha.</p>
<p>Além disso tem soluções como o Google Firebase que, como o Google App Engine antes dele, você programa especificamente pra essa plataforma, e vai depender pra sempre do que o Google te oferecer. Esquece querer mudar pro Azure ou AWS depois. E esquece querer usar qualquer ferramenta open source e bibliotecas que a plataforma não suporta. Tudo tem prós e contras e meu ponto é que o Heroku oferece o melhor custo benefício entre conveniência, segurança e independência do seu projeto.</p>
<p>Também vale relembrar que Heroku e 12 fatores não são balas de prata, eu tenho um caso pra ilustrar. Lá por 2014 estava pessoalmente trabalhando num projeto pra cliente que envolvia uma ferramenta que seria usada por vestibulandos. Era uma plataforma de educação e os donos tinham a expectativa que ia vir uma montanha de alunos de cursinho por causa de algum evento que eu não lembro mais o que era. Mas em resumo eles pediram especificamente pro sistema ser escalável.</p>
<p>Eu e minha equipe fizemos tudo em Ruby on Rails, que é o framework que funciona melhor no Heroku, especialmente naquela época, porque o próprio Heroku é em boa parte feito em Rails também. Fizemos deployment mas na hora do vamos ver sofremos um gargalo. Obviamente foi culpa minha que não estimou direito o tamanho do bancos de dados Postgres.</p>
<p>O problema é o seguinte: todo servidor de banco de dados tem um máximo de conexões simultâneas que aguenta. Faz de conta que são 100. Mesmo implementando um connection pool, se eu escalar horizontalmente a aplicação, ou seja, aumentar o número de dynos web rodando, todos vão se pendurar no banco de dados e rapidamente 100 conexões vão ficar ocupados e novos dynos vão ficar travados esperando conseguir uma conexão.</p>
<p>Pra lidar com isso a gente tenta mover o que não precisa pegar direto do banco em caches como Redis. E se precisa muito do banco, o certo é subir vários outros servidores que são réplicas só de leitura do principal. Assim cada nova réplica aguenta mais 100 conexões simultâneas, por exemplo. Mas não dá pra colocar réplicas infinitamente porque quanto mais réplicas tem, maior o trabalho de replicação dos dados, aí vai dar gargalo na sincronia de replicação.</p>
<p>Muito iniciante que entra em plataformas como Heroku achando que vai colocar um addon de auto-scale e tudo vai escalar infinitamente sozinho, se decepciona que não funciona mágico assim. Escalabilidade é uma combinação de funcionalidades de infraestrutura e arquitetura bem implementada. Depende mais do programador do que da infraestrutura. Mas se você for um bom programador, um Heroku dá tudo que você precisa com o mínimo de esforço. É impressionante o que eu consigo fazer sozinho num ambiente desse em 5 min, coisa que no começo dos anos 2000 ia me custar dias de configuração. Já se você for um programador ruim, nenhum Heroku ou solução de cloud vai te ajudar, só vai ser um band-aid temporário.</p>
<p>Eu já venho repetindo isso faz alguns episódios e vou repetir de novo. Eu tenho projetos pra clientes que funcionam assim, mas o certo como já disse antes é que os ambiente de staging e produção não estejam muito longe da versão em desenvolvimento na máquina dos desenvolvedores. Pra que isso seja possível, precisa obrigatoriamente existir os seguintes passos.</p>
<p>Primeiro passo, toda nova funcionalidade e toda correção de bugs deve vir acompanhado no mínimo de testes unitários. Todo pull request sem testes deve ser automaticamente rejeitado. Sem exceções. Essa é a principal regra que vai garantir que seu projeto sobreviva por muitos anos. No começo, com pouca gente, com pouco código e com prazos apertados, parece que é uma grande perda de tempo. E é assim que amadores pensam e é assim que amadores se ferram quando o projeto rapidamente cresce fora de controle.</p>
<p>Quando você satisfaz essa primeira condição agora precisamos do segunda passo: que é integrar o código constantemente e frequentemente. Ou seja, nenhum desenvolvedor deve ficar dias e dias com código que só existe na sua máquina, demorando pra dar push pro repositório no GitHub ou GitLab. Todo desenvolvedor deve, no mínimo uma vez por dia, dar pull, ou seja, puxar as últimas atualizações do repositório pra pegar tudo que todo mundo da equipe andou trabalhando e dar rebase com seu branch de desenvolvimento e corrigir conflitos. E com sua suite de testes unitários, rapidamente ver o que quebrou e consertar imediatamente. Se você faz isso com frequência, os conflitos são simples de resolver, se der conflitos. Quanto mais tempo demora pra integrar, mais difícil vai sendo pra resolver os conflitos do seu código com dos outros programadores da equipe, e maiores as chances de fazer bosta e subir bugs.</p>
<p>Isso é integração contínua. Facilita muito se como regra número três você estiver usando algo como GitLab que tem suporte a rodar testes automaticamente no servidor, ou usar qualquer outra solução como CircleCI. Você deixa um Dockerfile configurado no projeto, o GitLab vai buildar toda vez que alguém der push pro repositório e colocar numa fila pra rodar todos os testes automatizados. Se falhar já notifica pra todo mundo e assim garantimos que todo código que está na branch <code>main</code> sempre tem todos os testes rodando e passando. O objetivo é esse: que sempre que alguém der clone do branch <code>main</code>, tudo sempre vai funcionar. O branch <code>main</code> tem que ser sagrado. Tudo que está inacabado deve estar em branches de desenvolvimento separados.</p>
<p>É essa condição que permite a próxima regra número quatro que é, toda vez que todos os testes automatizados passarem, deve ser feito deployment pra um ambiente de staging. O Heroku suporta criar pipelines de staging e production, assim você sempre tem uma infraestrutura idêntica de testes e de produção. Daí um GitLab da vida mesmo faz o <code>git push heroku</code> automaticamente pra staging e sua equipe pode imeditamente testar no navegador como um usuário normal pra ver se realmente nada quebrou, se as novas funcionalidades estão mesmo funcionando.</p>
<p>Recapitulando: uma equipe saudável e produtiva vai sempre desenvolver código com testes automatizados, vai sempre integrar continuamente, vai sempre ter um servidor de testes como GitLab que roda a cada push, de cada desenvolvedor. E finalmente, vai sempre ter deployments automáticos pra ambiente de staging toda vez que todos os testes automatizados passam. E fica como opção subir pra produção automaticamente ou deixar alguém de QA,
por exemplo, responsável por fazer testes manuais no final e apertar o botão pra deployar pra produção e liberar pros usuários.</p>
<p>Um modelo similar ao Heroku, com um projeto arquitetado pra atender os 12 fatores, e com minhas 4 últimas regras é o que eu chamo do framework mínimo pra toda equipe de desenvolvimento verdadeiramente Ágil. Toda outra metodologia, sistema de backlog e controles, é opcional e não relevante pra produtividade de verdade. Lembrem-se, a qualidade do software é diretamente proporcional à qualidade da comunicação da equipe. Não tem como fugir disso. Se sua equipe se comunica mau e porcamente, seu software vai ser necessariamente uma grande droga.</p>
<p>A idéia hoje foi só apresentar pra quem é iniciante como que profissionais de verdade trabalham ou gostariam de trabalhar. E é esse nível que vocês deveriam almejar alcançar. Se você trabalhar num lugar que não funciona assim, tente evangelizar o jeito certo. Vai implementando um aspecto de cada vez, vai fazendo limpeza um pedaço de cada vez. Mesmo nos projetos mais antigos e mais porcos é possível transformar num projeto eficiente se, primeiro, a equipe entende que dá pra melhorar e se compromentem a fazer limpeza e não aumentar mais a sujeira. Por hoje é isso aí. Se ficaram com dúvidas mandem nos comentários abaixo, se curtiram o video deixem um joinha, assinem o canal e compartilhem o video com seus amigos. A gente se vê, até mais.</p>
]]></content:encoded><category>arquitetura-de-software</category><category>devops</category><category>desenvolvimento-web</category><category>akitando</category></item><item><title>Examinando o Vazamento do Twitch</title><link>https://akitaonrails.github.io/2021/10/21/examinando-o-vazamento-do-twitch/</link><guid isPermaLink="true">https://akitaonrails.github.io/2021/10/21/examinando-o-vazamento-do-twitch/</guid><pubDate>Thu, 21 Oct 2021 11:18:00 -0300</pubDate><description>&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/IBWdTuc6vDg"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2&gt;DESCRIÇÃO&lt;span class="hx:absolute hx:-mt-20" id="descrição"&gt;&lt;/span&gt;
&lt;a href="#descri%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;No começo de Outubro de 2021 vazou um &amp;ldquo;arquivão&amp;rdquo; de 120 gigas de coisas do Twitch que incluia MUITO código-fonte. Hoje vou dar minha opinião sobre esse vazamento e dar uma olhada no que tinha nesses códigos.&lt;/p&gt;
&lt;p&gt;Avisando: eu NÃO apoio vazamentos ilegais. Se alguém postar o link pra esses arquivos, será banido.&lt;/p&gt;
&lt;h3&gt;Conteúdo&lt;span class="hx:absolute hx:-mt-20" id="conteúdo"&gt;&lt;/span&gt;
&lt;a href="#conte%c3%bado" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;00:00 - Intro&lt;/li&gt;
&lt;li&gt;01:04 - Avisos Legais&lt;/li&gt;
&lt;li&gt;02:33 - Streamers não entenderam&lt;/li&gt;
&lt;li&gt;03:47 - Twitch + Amazon&lt;/li&gt;
&lt;li&gt;04:10 - O que é o vazamento?&lt;/li&gt;
&lt;li&gt;05:41 - Scriptando a avaliação&lt;/li&gt;
&lt;li&gt;06:33 - Limpando repositórios&lt;/li&gt;
&lt;li&gt;07:03 - Indexando os arquivos&lt;/li&gt;
&lt;li&gt;08:04 - Vendorização e dependências&lt;/li&gt;
&lt;li&gt;13:12 - Limpando vendorização&lt;/li&gt;
&lt;li&gt;15:13 - Depois da Limpeza&lt;/li&gt;
&lt;li&gt;16:14 - Do Justin.tv ao Twitch&lt;/li&gt;
&lt;li&gt;22:04 - Protobufs&lt;/li&gt;
&lt;li&gt;23:50 - Infraestrutura&lt;/li&gt;
&lt;li&gt;24:54 - Projetos ativos hoje&lt;/li&gt;
&lt;li&gt;26:42 - Como startups evoluem&lt;/li&gt;
&lt;li&gt;28:11 - Quando usar Microsserviços?&lt;/li&gt;
&lt;li&gt;30:25 - Ranking de Linguagens&lt;/li&gt;
&lt;li&gt;32:21 - Conclusão&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;SCRIPT&lt;span class="hx:absolute hx:-mt-20" id="script"&gt;&lt;/span&gt;
&lt;a href="#script" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Olá pessoal, Fabio Akita&lt;/p&gt;</description><content:encoded><![CDATA[

<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/IBWdTuc6vDg"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<h2>DESCRIÇÃO<span class="hx:absolute hx:-mt-20" id="descrição"></span>
    <a href="#descri%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>No começo de Outubro de 2021 vazou um &ldquo;arquivão&rdquo; de 120 gigas de coisas do Twitch que incluia MUITO código-fonte. Hoje vou dar minha opinião sobre esse vazamento e dar uma olhada no que tinha nesses códigos.</p>
<p>Avisando: eu NÃO apoio vazamentos ilegais. Se alguém postar o link pra esses arquivos, será banido.</p>
<h3>Conteúdo<span class="hx:absolute hx:-mt-20" id="conteúdo"></span>
    <a href="#conte%c3%bado" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li>00:00 - Intro</li>
<li>01:04 - Avisos Legais</li>
<li>02:33 - Streamers não entenderam</li>
<li>03:47 - Twitch + Amazon</li>
<li>04:10 - O que é o vazamento?</li>
<li>05:41 - Scriptando a avaliação</li>
<li>06:33 - Limpando repositórios</li>
<li>07:03 - Indexando os arquivos</li>
<li>08:04 - Vendorização e dependências</li>
<li>13:12 - Limpando vendorização</li>
<li>15:13 - Depois da Limpeza</li>
<li>16:14 - Do Justin.tv ao Twitch</li>
<li>22:04 - Protobufs</li>
<li>23:50 - Infraestrutura</li>
<li>24:54 - Projetos ativos hoje</li>
<li>26:42 - Como startups evoluem</li>
<li>28:11 - Quando usar Microsserviços?</li>
<li>30:25 - Ranking de Linguagens</li>
<li>32:21 - Conclusão</li>
</ul>
<h2>SCRIPT<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>Eu normalmente não faço videos sobre assuntos da moda porque esse tipo de video tende a envelhecer muito rápido. Mas um episódio que aconteceu alguns dias atrás chamou minha atenção e acho que todo mundo ficou sabendo. O vazamento de 120 gigabytes de dados de ninguém menos que o Twitch.</p>
<p>A primeira semana de Outubro de 2021 vai ficar marcado na história porque no começo dessa semana, alega-se que por problemas de DNS o Facebook e seus outros apps como Instagram e Whatsapp ficaram fora do ar por metade do dia, coincidentemente um dia antes de um vazamento de outra natureza que expôs suas táticas de negócios. Mas esse assunto não me interessa tanto pra discutir no canal, deixo isso pros twiteiros.</p>
<p>E logo na sequência veio o vazamento do Twitch. E eu quero falar sobre esse último episódio porque acho fascinante pra quem nunca trabalhou numa tech startup grande ter uma noção de como é o software por trás. Talvez quebrar algumas concepções erradas que muitos iniciantes ainda tem.</p>
<p>(&hellip;)</p>
<p>Obviamente tenho que começar com um disclaimer gigante aqui: eu não apoio atividades ilegais como vazar dados de qualquer empresa ou pessoa. Eu não admiro nem um pouco quem fez isso, muito pelo contrário, eu condeno como um criminoso comum. Nem mais, nem menos. Se for pego e condenado só vou pensar que não foi mais que merecido.</p>
<p>E não adianta vir nos comentários perguntar como se pega esses arquivos. Se alguém compartilhar links, qualquer um, eu vou banir na mesma hora, então já fiquem avisados. E nem fiquem me enchendo o saco por DM, vou só ignorar. Quem tá interessado, se vira.</p>
<p>Possuir os arquivos é uma zona cinzenta. Dependendo da jurisdição, das circunstâncias, pode não ser bom. Por isso eu não pretendo manter esses arquivos, depois do video vou dar o bom e velho <code>rm -Rf</code>. Usar o código é totalmente fora de cogitação. É propriedade intelectual de terceiros. Você nunca pode usar código que você mesmo fez se foi dentro de um contrato com outra empresa. Se não gosta dos termos, não assine o contrato e nem o pagamento. Se você tem dúvidas com relação à lei, consulte um advogado.</p>
<p>Eu conheço as leis, e pra garantir eu consultei meu advogado pra ter certeza. Se alguém tem interesse comece estudando a Lei de Software, Lei 9.609/1998. Mas a parte que me interessa é que não constitui ofensa aos direitos do titular de programa de computador a citação parcial do código-fonte para fins didáticos, desde que identificados o programa e o titular dos direitos respectivos. E também nada do que vou dizer neste video deve ser interpretado como tentativa de difamação ao Twitch.</p>
<p>Eu brinquei no Twitter sobre o que vocês achariam se alguém hipotético que tem esse código fizesse um code review ao vivo num streaming. Obviamente eu tava brincando. Talvez não seja totalmente ilegal mostrar trechos, mas eu considero anti-ético. Qualquer um que tentou fazer isso e foi banido do Twitch ou outras plataformas, mereceu. Isso é molecagem, coisa de criança de 5 anos mesmo cheirando a leite.</p>
<p>Além disso, eu duvido que qualquer um que ficou biscoitando tentando fazer live entendeu qualquer coisa útil dessa montanha de código. Só quem já trabalhou num ambiente assim e lidou com projetos desse tamanho sabe o que procurar e como. E já antecipo que apesar de não ter tido tempo de ver 100%, não tem nada de mais nesse código. Nenhum grande segredo. Nenhuma mágica especial. É muito parecido com dezenas de outros projetos que eu já vi ao longo dos anos.</p>
<p>Se você é iniciante, este video vai ser mais uma lista de coisas que vocês precisam estudar. Se você já é experiente, o video vai ser mais uma confirmação do que vocês já tinha deduzido. Não é muito difícil de deduzir porque todo mundo sabe que ela foi comprada pela Amazon, portanto tem todo o interesse de usar tudo que a Amazon Web Services, a AWS oferece, e sim, de fato ela usa o máximo possível de tudo do AWS, em particular o Kinesis porque é streaming.</p>
<p>Assim como todo mundo ela também usa o S3 pra servir arquivos, o Redshift que é uma base de dados colunar usada pra datastore de big data, Route53 pro DNS, SNS pra notificações, SQS pra fila, DynamoDB pra dados que precisam acessar rápido, Redis pra cache rápido, Postgres como banco de dados estruturada, CloudWatch pra métricas e por aí vai. Nada de novo até aqui.</p>
<p>Antes de continuar vale a pena eu falar um pouco sobre a natureza desse código e o que de fato tem nesse arquivão. Como já expliquei, isso foi chamado de &ldquo;parte um&rdquo;. Talvez tenha uma parte 2, talvez não. Independent, a gente teria que assumir que o que vazou não foi 100%, porque quem vazou não se identificou e não sabemos que nível de conhecimento e de acessos tinha. Por isso é seguro afirmar que esse vazamento é com certeza incompleto. Mas mesmo assim dá pra tirar alguns insights.</p>
<p>Se você não tá acostumado, pode pensar, &ldquo;caraca, é incompleto e mesmo assim tem 120 gigas de material?&rdquo; Na verdade não. O que vazou foram 159 arquivos zipados que dão 120 giga. Se descompactar tudo dá mais de 180 gigas. E não é mais porque misturado tem muito arquivo binário, como imagens, que não compacta bem. Se fosse só texto, a taxa de compactação seria de quase 10 pra 1, mas aqui é menos de 2 pra 1. Pra mergulhar nisso tudo, precisa de um bom SSD porque vasculhar tudo manualmente é impossível. Estamos falando de milhares de diretórios e quase na ordem de centenas de milhares de arquivos.</p>
<p>Sinceramente, eu não tenho paciência pra ficar olhando um a um cada diretório. Um time inteiro precisaria de meses pra auditar linha a linha na mão. Eu me limitei a não mais uns 3 dias nisso. Então eu tive que usar a estratégia de jack, o estripador. Ir por partes. Abrir tudo isso de uma só vez num Visual Studio Code da vida e tentar procurar palavras é uma estupidez. Cada procura demora minutos ou mais. De novo, são literalmente milhões de arquivos. Mais exatamente 3.5 milhões de arquivos.</p>
<p>Em vez disso eu fiz um script que usa a ferramenta <code>find</code> que tem em qualquer Linux e montei um banco de dados em SQLite3. Só pra guardar coisas básicas: o caminho completo ou path, a extensão do arquivo, quantos bytes ele tem de tamanho, coisas assim. Meu script ingênuo levou mais de 30 horas pra indexar tudo e gerou um SQlite de quase 2 gigabytes. Mas com isso agora eu posso fazer queries SQL que responsem em milissegundos pra achar o que eu quero.</p>
<p>Um script pra isso pode ser feito no que você quiser, em Bash mesmo do seu shell, em Python, no caso eu fiz rapidinho em Ruby. Pra fazer esse tipo de coisa é ótimo usar linguagens como Python ou Ruby. Inclusive, uma boa parte do Python que tem nesse código vazado é exatamente isso: scripts pra automatizar tarefas mundanas como limpar arquivos, conectar em serviços de logs ou analytics pra limpar os dados e mandar pra um banco de dados ou outro armazenamento como Spark.</p>
<p>Na verdade antes de indexar tudo eu fiz uma primeira limpeza. Eu notei que muitos projetos ainda tinham o diretório <code>.git</code>, que se você assistiu meus videos sobre Git, sabe que é um basicamente um banco de dados de commits que contém o histórico das mudanças desse projeto. E esse diretório ocupa bastante espaço. Então eu fiz primeiro um script que move todos os <code>.git</code> pra outro lugar. E como eu esperava, isso removeu mais de 60 gigas. Então em vez de 180 gigas descompactado, na verdade o que temos pra trabalhar são 126 gigas.</p>
<p>Uma vez feito isso, daí eu rodei o script pra indexar num sqlite3 e isso que totalizou os 3.5 milhões de arquivos, já removido todos os arquivos dos diretórios <code>.git</code>, mas tem ainda um monte de coisa que não é código fonte. Inclusive eu tenho uma suspeita que seja lá quem foi o perpetrador que saiu copiando as coisas, eu acho que é ou ex-funcionário ou ele entrou nas máquinas de desenvolvimento de funcionários, porque não são clones de git limpos, eles tem um monte de arquivos temporários, como builds de android, builds de go, coisas de javascript depois que rodou <code>npm install</code> porque tem coisas como diretórios <code>node_modules</code>, diretórios de dependências, um monte de binários que sobra depois de baixar bibliotecas open source e compilar.</p>
<p>Aliás, além de bibliotecas de dependências baixadas, também tinha um monte de bibliotecas open source vendorizadas manualmente. Isso é uma má prática e você deve evitar ao máximo fazer isso. Aqui vale uma tangente. Se você nunca ouviu falar de vendorização, ouça o tio Akita: não vendorize coisas de terceiros no seu projeto. E o que é vendorizar?</p>
<p>Digamos que você esteja fazendo um script que vai encriptar arquivos no seu sistema. Pelo menos você não é um burro completo e não vai tentar criar seu próprio algoritmo caseiro de criptografia, o que seria uma estupidez sem tamanho se você não for estritamente um pesquisador renomado e com anos de experiência na área. Se não entendeu isso, assista meus episódios sobre criptografia, mas em resumo, 100% das vezes que um programador comum tenta criar criptografia do zero, é 100% de certeza que é um lixo cheio de buracos de segurança.</p>
<p>Daí você resolve baixar o código fonte do OpenSSL ou alternativas como o BoringSSL, que são bibliotecas reconhecidas, com diversas funcionalidades de criptografia, geração de par de chaves e tudo mais. Daí você evitou uma catástrofe de usar uma criptografia caseira escolhendo uma das bibliotecas mais usadas do mundo, portanto ordens de grandeza mais seguro do que o que você faria sozinho. Mas acabou criando outra catástrofe esperando pra acontecer: você copiou o código do OpenSSL dentro do seu projeto e embutiu ele no seu sistema. Essa é um péssima decisão.</p>
<p>Digamos que a versão do OpenSSL que você embutiu foi a 1.0. Ah, que mau pode fazer? Pelo menos assim eu sei que tudo funciona, eu não dependo de ter que baixar a biblioteca quando for buildar nem quando instalar num servidor, facilitou minha vida. Errado. Vira e mexe descobrem bugs de segurança, agora mesmo em agosto foi descoberto mais um, que foi corrigido nas versões 1.1.1 e 1.0.2.</p>
<p>Só que você não é um especialista em segurança, você terminou aquele código e já esqueceu dele. E agora sobrou um software em produção rodando com OpenSSL 1.0 com buracos de segurança que todo mundo já conhece. A versão mais recente já é a 3.0. E como você é esperto pra caramba, saiu embutindo o código do openssl em todas as dezenas de projetos que precisava de alguma coisa de criptografia. Agora tem dezenas de projetos, cada um rodando uma versão diferente de openssl e nenhum sendo atualizado.</p>
<p>Isso é pior ainda quando você usa uma biblioteca &ldquo;wrapper&rdquo; como a cryptography de Python que por baixo dos panos é só uma casca pro OpenSSL nativo. E você nem sabia disso de openssl, só vendorizou a biblioteca cryptography dentro do projeto e não pensou duas vezes que podia vir uma bomba relógio junto. Projetos mal feitos de Python vira e mexe tem esse problema. Em vez de usar um gerenciador de pacotes como um PIP da vida, tem gente que ainda baixa e deixa estático no projeto.</p>
<p>E de fato, eu achei dezenas de lugares com a biblioteca cryptography, openssl, além de várias outras como url3lib, setuptools e mais. Aliás, em vários lugares eu achei as instalações inteiras de python2.7, python3.6 e 3.7 mas eu acho que nesse caso foi diretórios de build de pacotes de linux ou algo assim. Acho que não tava embutido nos projetos, e por isso eu disse que o vazamento foi não direto dos servidores de repositórios, mas sim da máquina de desenvolvimento de algum programador.</p>
<p>Enfim, a tangente foi mais pra ilustrar que a gente usa um troço chamado gerenciador de dependências por uma razão: não desperdiçar espaço baixando bibliotecas externas e também pra termos controle pra atualizar essas bibliotecas no futuro. Fazendo direitinho, ou seja, declarando a biblioteca e qual versão seu projeto precisa em arquivos como <code>package.json</code>, num projeto de Node.js, num <code>Gemfile</code> num projeto de Ruby, num <code>requirements.txt</code> num projeto de Python e assim por diante, depois temos como criar ferramentas de auditoria que conseguem ver se algum projeto tá usando uma versão muito antiga de alguma coisa que pode ser um problema de segurança. A ferramenta dependabot do GitHub faz exatamente isso.</p>
<p>Algumas comunidades como Go e Python tem problemas históricos de gerenciar versões de bibliotecas. Em python nem todo mundo usa coisas como PIP, muitos dependem de pacotes da distribuição de Linux que usam, o que pode gerar várias dores de cabeça se usar em distros diferentes. O Go antigamente era uma porcaria pra gerenciar versões, só a partir da 1.11 que isso melhorou um pouco, porque antes você declarava a biblioteca e ele baixava e vendorizada direto do github, mas não marcava a versão, então era um saco de gerenciar dependências. Esse foi um erro grave que os autores cometeram nas primeiras versões e isso gerou diversas ferramentas de terceiros com jeitos diferentes de resolver esse problema e muita falta de consenso por muito tempo.</p>
<p>Falando em falta de consenso, Javascript até poucos anos atrás também era uma dor de cabeça. Quem lembra de coisas como Bower? Os eternos bikeshedding sobre formato de pacotes? A lentidão na evolução do NPM antigo, tanto que o Facebook criou a própria dela, o Yarn, e isso forçou o NPM a andar mais rápido. E uma das coisas que mais me deixa irritado é desenvolvedor que não coloca o diretório <code>node_modules</code> no arquivo <code>.gitignore</code> como deveria e aí quando baixo o projeto vem uma tonelada de arquivos inúteis. Pessoal, bota a mão na consciência, não commita <code>node_modules</code>, é pra isso que existe o <code>npm</code> ou <code>yarn</code>. Aprende a usar.</p>
<p>Isso tudo foi só pra argumentar porque eu disse que esse código vazado talvez seja da máquina de desenvolvimento de alguém, porque tá cheio de diretórios de coisas vendorizadas, ou seja, depois que alguém rodou um <code>npm install</code> da vida e baixou as bibliotecas. Portanto, eu tinha que primeiro sair apagando essas bibliotecas do caminho. E tinha um monte de coisas.</p>
<p>No começo eu fiz um script que vasculhava os arquivos de LICENSE. Daí se dentro tinha palavras como &ldquo;Apache License&rdquo; ou &ldquo;BSD License&rdquo; ou &ldquo;GNU&rdquo; ou coisas assim, eu sabia que eram bibliotecas open source. O problema é que isso você acha no repositório oficial num GitHub da vida, mas depois que eles empacotam pra distribuir num servidor de NPM ou Maven da vida, o script de build costuma excluir arquivos que não são essenciais, incluindo os de licença, então a maioria das coisas vendorizadas não tinha esse arquivo. Então isso dificultou um pouco na minha limpeza.</p>
<p>Daí não teve muito jeito, eu fui listando arquivos de terminadas extensões como .C ou .CPP que é C++ e isso me levou a bibliotecas fáceis de reconhecer como ffmeg, libxml, zlib, tcmalloc, libpng, boringssl e muito mais. Se você mexe como desenvolvimento faz tempo, tem centenas de nomes assim que imediatamente você sabe que é open source. Tudo isso eu apaguei do meu banco de dados. No final meu script ficou com uma lista de padrões que eu ia pesquisando no caminho completo de cada arquivo e marcando pra apagar. Olha só como ficou.</p>
<p>Pra achar esses padrões, foram algumas horas, só vasculhando, fazendo queries no meu índice, e apagando tudo que eu achei que fosse open source, coisas vendorizadas, artefatos temporários de builds automatizadas. E isso faz diferença, por exemplo, tinha código fonte dos apps de iOS, Xbox, Android e dentro tinha diretórios como <code>android-dev-tools</code>, diretório dos SDKs, e quem já desenvolveu Android sabe que só um diretório de SDK ocupa por volta de 1 giga de espaço.</p>
<p>Eu acho que limpei pelo menos a grande maioria, mas muita coisa ainda pode ter sobrado. Pode ser que eu tenha apagado uma coisa ou outra que não era open source por acidente, mas isso deve ser suficiente pra dar uma idéia geral. Recapitulando, o total do vazamento foram 120 gigas compactados que dão uns 180 descompactados. Daí eu ignorei diretórios como <code>.git</code> e depois de uma primeira limpeza de bibliotecas open source, cheguei a um total de mais de 3.5 milhões de arquivos que somam uns 110 gigabytes.</p>
<p>Daí limpando o que eu achei que era open source, builds, coisas como backups de banco de dados, imagens, e tudo mais, no final sobraram ainda mais de 600 mil arquivos totalizando uns 11 gigabytes. Ou seja, deu pra jogar quase 90% fora pra conseguir separar o que é realmente proprietário do que era open source e coisas temporárias. Como já disse, a limpeza manual que eu fiz não foi 100% preciso, mas acho que cheguei próximo o suficiente pra conseguir tirar as informações que eu queria.</p>
<p>Se você não entendeu isso de sqlite3 ainda, é uma tabela com o caminho completo de cada arquivo, diretórios, subdiretórios, nome do arquivo e extensão, além do tamanho de cada arquivo. fazendo isso eu consigo fazer, por exemplo, <code>select count(*), sum(size) from files where extname = '.go'</code> ou em português, pesquise na tabela <code>files</code> a quantidade de arquivos e o tamanho total deles em bytes onde os arquivos tem extensão <code>.go</code>. E com isso eu consigo saber quantos arquivos de Go existem e quanto bytes ocupam. E posso fazer isso pra todas as linguagens que achei.</p>
<p>Sim, linguagens no plural. Se você é iniciante, isso pode ser um choque. Sim: toda grande tech startup usa mais de uma linguagem. Sem nem ver o código fonte, não é difícil de imaginar quais linguagens se usa e por quais razões. Por exemplo, eu já sabia que o Twitch era originalmente feito em Ruby on Rails, quando ela ainda se chamava Justin.TV. Aliás, no código hoje ainda tem muitos pacotes com namespace justin.tv.</p>
<p>À medida que a plataforma começa a dar certo você começa a expandir pra outras coisas. Por exemplo, o Twitch consegue exportar videos pro YouTube. Pra fazer isso você usa ferramentas open source como <code>ffmpeg</code>, o software mais importante de encoding e decoding de video, que todo mundo usa e, adivinha, o Twitch também usa. Pra processar áudio de voz, tem a biblioteca <code>speex</code> e assim vai, tem dezenas de ferramentas pré-prontas e não só o Twitch mas qualquer um vai usar todas elas. E depois de um certo tamanho você quer entender como esse código funciona, e eles são feitos em C++.</p>
<p>Se sua startup sobrevive e cresce, agora você tem milhões de requisições, precisa subir dezenas de servidores, e vai precisar otimizar performance. Uma das primeiras coisa que a gente faz em linguagens como Ruby e Python é se preocupar com o gerenciador de memória, que deriva de C, o malloc padrão. Em vez dele a gente tenta outros gerenciadores como jcmalloc do Facebook ou tcmalloc do Google. Eu falei disso no episódio de gerenciamento de memória. E de fato, vi rastros de tcmalloc no projeto.</p>
<p>Pra diminuir o tempo de cada requisição web, tem duas coisas que a gente faz logo de cara. Primeiro joga tudo que tá gerando gargalo em jobs em background. No mundo Rails a gente usa bibliotecas como Sidekiq ou Resque. Eles mantém uma fila separada num banco como Redis, ou mesmo no Postgres - o que eu não recomendo, a não ser que conecte numa instância de banco separada da instância principal, ou conecta num serviço de filas como o SQS da Amazon. Daí a requisição web só tem o trabalho de gravar na fila do banco e ir embora muito rápido, e deixar o trabalho mais demorado pra um worker em background, que vai pegar o job da fila e processar depois. Isso é padrão, todo mundo faz isso. Eu falei um pouco disso no episódio sobre Concorrência e Paralelismo.</p>
<p>A segunda coisa é separar queries demoradas, pesquisas que custam mais caro que estão sendo feitas no seu banco de dados tradicional como Postgres e jogar os dados mais acessados, já pré-calculados e consolidados, num banco de dados mais rápido NoSQL como Redis ou DynamoDB. E adivinhem, é o que o Twitch está fazendo. Eu falei mais disso no episódio sobre NoSQL. E não, jogar tudo num NoSQL não é a solução. Alguns bancos são otimizados pra escrever rápido, mas as pesquisas são lentas. Como um Cassandra. Outros são otimizados pra leituras rápidas, mas a escrita é lenta, como um MongoDB. Outros ainda são otimizados pra não perder dados, mas a leitura e escrita podem ser lentos, como um Postgres. Cada caso precisa de um banco diferente ou uma combinação deles, o que é mais comum.</p>
<p>Mas só isso não é o suficiente quando você chega num certo patamar de audiência, milhões de pessoas penduradas o dia inteiro assistindo ASMR e moleques jogando Fortnite. Apesar de ser altamente produtivo, uma plataforma dessas não tem como ser 100% em Rails, e agora você começa a gerar microserviços em outra coisa, como Go. Mas nesse estágio, agora você tem tamanho, experiência e dinheiro pra começar a criar ferramentas proprietárias pra lidar com coisas como WebSockets e WebRTC pra conseguir manter seus chats no ar.</p>
<p>Mas não é só isso. Com Rails você pode continuar fazendo coisas como painéis de administração, suporte, configuração e funcionalidades do site que vão ser estáticas e podem ser servidas por cache. Nesse estágio você começa a ter volume grande de dados, big data, e precisa lidar com isso. Então começa a criar ferramentas pra consumir esses dados. Logs pra auditoria, logs pra ads, logs pra analytics, e precisa tratar, consolidar e processar esses dados em coisas como Redshift, Spark, monitorar a infra com coisas como Ganglia, notificações de Rollbar, criar dashboards que mostram esses dados com coisas como Grafana. Novamente, tudo que todo mundo nesse tamanho usa, e o Twitch não é diferente.</p>
<p>À medida que você vai adicionando mais peças, mais serviços externos, mais microserviços internos, vai aparecer um volume grande de um tipo comum de código: wrappers, ou clientes de APIs. Uma boa parte do código dos projetos é só um codigozinho que chama uma API e trata o resultado de alguma forma. E sendo o Twitch parte da Amazon, não é surpresa que a maior parte é código pra integrar com Cloud Watch, com o SNS, SQS, enfim tudo que a AWS oferece. E de fato, muito do que eu falei que precisei apagar pra fazer a contagem eram diretórios como <code>aws-sdk</code>.</p>
<p>E ao longo do tempo da startup, vão surgindo campanhas temporárias. Uma campanha patrocinada, colaboração com marcas, promoções e tudo mais. Código que só vai servir por alguns meses e depois sai do ar. E de fato, tem vários projetos desse tipo que claramente estão arquivados e não são mais usados. Esbarrei em alguns wordpress, com um tanto de plugins. Mas não sei se era código ativo. PHP é uma das coisas menos usadas no Twitch. Depois da limpeza só achei uns 2 mil arquivos que não dava 11 megabytes, do total de 11 gigas. E felizmente muitos deles foram feitos como projetos em repositórios separados dos principais, o que ajuda a não poluir demais o código do dia a dia.</p>
<p>Pra que seja possível separar as coisas em micro projetos diferentes, uma outra parte do código vai ser pra expôr seus serviços internos como APIs, tanto APIs HTTP, web services, que normalmente respondem JSON da vida e aí fica mais fácil criar sites separados de campanhas. Dá pra facilmente integrar no front-end um sistema de autenticação, puxar dados do perfil do usuário e tudo mais. Isso é bem comum também e aqui não é diferente. Mas o que é diferente é um cuidado que pouca gente toma e só faz sentido em sistemas grandes: a comunicação entre micro serviços.</p>
<p>Só quando a startup vai chegando mais perto do tamanho de um Twitch, cada milissegundo, cada kilobyte conta, porque são milhões de operações por segundo na plataforma inteira. Sistemas menores não devem se preocupar com isso, mas neste caso eles usam muito Protobuf. Em resumo, um microserviço é basicamente uma aplicação web pequena que expõe APIs e se integra com outros micro serviços chamando as APIs deles. Sendo uma aplicação web, o protocolo de comunicação é HTTP e o pacote de resposta costuma ser JSON ou XML.</p>
<p>JSON é ótimo pra um ser humano entender, por isso toda grande plataforma expõe APIs que respondem JSON ou similar pra facilitar pro pequeno desenvolvedor integrar sem muito trabalho. Mas é um tradeoff porque protocolos em texto desperdiçam muito espaço. Internamente na plataforma, o protocolo precisa ser rápido, mas sem perder totalmente as características de ser conveniente de implementar e integrar. E por isso o Google inventou o Protobuf, que é uma forma fácil de criar protocolos binários de RPC, ou Remote Procedure Call.</p>
<p>Uma estrutura de dados convertida em texto desperdiça muito espaço, mesmo se compactar com gzip, o que adiciona tempo de processamento pra ficar compactando e descompactando. Um protocolo binário é muito mais eficiente porque você não fica convertendo números em string, daí ter que reconverter de volta em número. Em binário ainda dá pra montar vetores de bits e muito mais. Existe um padrão chamado gRPC que muita gente usa com Protobuf, mas o Twitch inventou o dele, que é open source e se chama Twirp. Vale a pena dar uma estudada, mas o ponto é que muita coisa usa esse protocolo binário internamente em vez de APIs HTTP. Eu sei disso porque na limpeza esbarrei com várias cópias da biblioteca de protobuf e twirp vendorizadas.</p>
<p>Como tem muito microserviço, começa a ficar complicado pro desenvolvedor rodar e testar cada um. Por isso normalmente todo projetinho desses acompanha instruções num arquivo README da vida, o que é uma boa prática e se você não faz, deveria se acostumar a fazer. E também costuma vir junto pelo menos um arquivo <code>Dockerfile</code> pra facilitar subir um container local pra rodar. Também é boa prática escrever arquivos como INSTALL ou BUILD ou até mesmo um <code>Makefile</code> de uma vez pra automatizar o build dos pacotes. Achei muito disso em vários dos diretórios que vasculhei.</p>
<p>Feito tudo isso, o problema agora é subir esse tanto de coisa em produção. E aqui eu fiquei um pouco na dúvida. Não achei absolutamente nada de Kubernetes. Sendo na Amazon o normal seria ter configurações de Terraform pra subir tudo no EKS que é o serviço de Kubernetes da Amazon. Mas nada. O que eu achei foi um monte de arquivos de configuração de Terraform (main, variables, output), mas parece que o provider é conectando direto nos serviços da AWS pra configurar coisas como RDS, EC2, Beanstalk e tudo mais.</p>
<p>Pode ser que eu não tenha olhado direito, ou faltou isso no vazamento, talvez uma das coisas que devem sair na parte 2. Eu cheguei a cogitar que talvez o vazamento saiu agora mas ele pegou as coisas faz tempo e só resolveu vazar muito tempo depois, mas não. Eu fiz outro script pra vascular todos os repositórios Git, os diretórios <code>.git</code> locais. Nem todo diretório tinha um <code>.git</code> mas rodando meu script eu encontrei quase 5 mil e 700 repositórios. Daí fiz um script pra rodar <code>git log</code> em cada um e conseguir a data do último commit. De novo, fiz meu script jogar tudo num outro SQLite3.</p>
<p>Quando meu script terminou, eu fiz um select agrupando o ano da data desses últimos commits e de fato, quase 2000 deles tem commits recentes de 2021, os mais recentes são de 4 de outubro deste ano, poucos dias antes do vazamento. Então são repositórios ativos neste momento mesmo. Tem repositórios que não tem commits novos faz tempo, mas mesmo assim quase 890 tem commits em 2020, 844 em 2019 e assim por diante. Mas mais da metade dos repositórios tem commits entre 2021 e 2020, ou seja, recentes. Aliás, fazendo essa query eu consigo saber quais projetos estão ativos neste momento e quais são projetos antigos que não se usa mais.</p>
<p>De qualquer forma, parece que pode ser só Terraform direto ligando na AWS sem usar Kubernetes no meio, controlando EC2 na mão mesmo. E não, eu não gosto de Kubernetes, na verdade acho horrível pra qualquer um que não seja do tamanho de um Twitch pra cima. Eu sempre penso que o Google inventou Kubernetes pra resolver o problema de datacenters do tamanho dos deles. Projetos pequenos e mesmo médios deveriam ficar longe de Kubernetes. A grande maioria dos projetos devem ficar num Heroku da vida, e já tá mais que ótimo. A maioria das pessoas inexperientes ignora os custos elevados de se gerenciar e manter uma infra toda em Kubernetes.</p>
<p>O que me leva ao ponto que falei lá no começo, uma grande tech startup, até mesmo média, vai usar mais de uma linguagem: a linguagem certa pra coisa certa. Normalmente vai ser ou Rails ou Node.js ou Django ou Laravel pra aplicações mais visuais, que usuários interagem. Isso porque no estágio inicial de uma startup, frameworks desse tipo são super produtivos, tem um ecossistema rico com soluções pré-prontas pra você não ficar reinventando a roda, e muita documentação e guias pra orientar o desenvolvimento. Ninguém deveria estar fazendo do zero autenticação, login, esqueci minha senha do zero, isso é pré-pronto em todo framework decente.</p>
<p>Depois disso começa a entrar coisas como Go ou Elixir, pra pedaços que precisam obrigatoriamente de baixíssima latência ou suportar milhares de pessoas penduradas em conexões de longa duração como WebSockets ou WebRTC, como é o caso de um Twitch, Discord, Messengers e coisas assim.</p>
<p>O front-end obviamente é sempre em Javascript. No caso como o Twitch foi feito em Rails lá atrás, ainda tem muita coisa usando o framework Ember. Não só isso, eu achei um monte de jquery vendorizado. Como nos últimos 15 anos o mundo javascript foi inventando um monte de bibliotecas e frameworks, qualquer startup com 10 anos ou mais vai ter uma torre de babel misturando jquery, ember, angular, react e tudo mais. Não fiquei fuçando muito a parte de Javascript, mas parece que é muito diferente disso. É difícil de ter uma contagem do que é javascript que eles fizeram e o que é coisa baixada misturada.</p>
<p>Muita gente tem dúvidas sobre organização de microserviços, quando deveria fazer, como deveria organizar. E isso não tem resposta exata. Micro serviços aparecem em duas ocasiões principais: quando você tem um endpoint específico que precisa de características muito especiais de baixa latência e alto throughput. E o segundo caso é quando você precisa dividir suas equipes internas pra facilitar o gerenciamento, daí divide o sistema em microserviços, um pra cada equipe, que é o caso clássico da Lei de Conway, que observou que com o tempo, a estrutura de um software tende a replicar a estrutura de comunicação da empresa.</p>
<p>Com algumas ressalvas, eu diria que quanto menos micro serviços você tem sempre é melhor. Muita gente gosta de ficar falando mau de monolitos, mas existem monolitos e monolitos. Outra tech startup gigante feita em Rails e que faz questão de se orgulhar de ser um monolito é o Shopify. Claro que também tem micro serviços, mas eles querem dizer que não saem dividindo o software em milhões de serviços à toa. A vantagem de um monolito é que a complexidade da infra tende a ser mais baixa, mas a complexidade do software pode dificultar a produtividade. O problema é quando o código é sujo, macarrônico, onde passou um monte de programador porco que largou um monte de dívida técnica, com quase nenhum teste automatizado, nenhum build automatizado, ou seja, um inferno de dar manutenção.</p>
<p>Um monolito bem cuidado pode durar muito tempo e costuma ser sinal de uma empresa que tem boa comunicação entre suas equipes e mantém boas práticas de código limpo. Eu não sei quantos programadores existem no Twitch hoje, mas eles tem muito mais microserviços do que eu gostaria de ver. Talvez tenham sido mais uma vítima da Lei de Conway. Mas sendo justo, uma parte do código que eu vi tem testes automatizados. E pelo menos a parte de infra parece decente, com scripts que automatizam muita coisa.</p>
<p>Falando nisso, só de bater o olho aleatoriamente, eu não vi código muito sujo além do normal de qualquer projeto. Nada excessivamente macarrônico, funções com dezenas de linhas, nomes de variáveis difíceis de identificar, números mágicos, complexidade ciclomática e coisas assim. Inclusive eles tem um guia de boas práticas de como escrever o código, o que costuma ser uma boa coisa pra ser criar internamente, pra todo mundo saber o que é esperado do código.</p>
<p>Enfim, vamos fazer um pequeno ranking só de brincadeira. Como eu disse antes, eu limpei o código o máximo que dava. Joguei fora tudo que eu achei que era open source. Tudo que claramente era código duplicado entre projetos e fazia muito volume e no final achei o seguinte. Dos 11 giga que sobrou, tem quase 58 mil arquivos de Go que totalizam uns 300 megabytes.</p>
<p>O segundo colocado, que não é surpresa pelo tanto de front-end que uma plataforma dessas costuma ter é javascript. Mas como eu disse antes, eu acho que ainda tem muita coisa vendorizada que eu não consegui limpar. São mais de 18 mil arquivos que totalizam 68 megas. Tecnicamente HTML seria o segundo lugar com quase 21 mil arquivos ocupando 450 megas, mas tem centenas de diretórios de documentação de bibliotecas com HTML no meio e coisas assim, então esse número tá bem inflado ainda.</p>
<p>Achei curioso que tem bastante arquivo de C#, e aqui eu fiquei ainda em dúvida se eu não limpei bibliotecas externas o suficiente mas a razão de ter muito C# é porque o Twitch tem apps nativos pra tudo que é plataforma como Windows e XBox. Tinha até o executável do NuGet espalhado em vários diretórios, muitas DLLs que eu imagino que seja do SDK e outras dos builds automatizados, mas no final da limpeza sobrou mais de 9 mil arquivos de C# ocupando uns 51 megas.</p>
<p>Eu falei que eles usam muito Terraform? Só de arquivos .tf de configuração tem quase 22 mil arquivos que consomem 37 megabytes. Arquivos de configuração passaram na frente de arquivos de Ruby que tem só uns 16 mil arquivos ocupando 32 megas e depois dele vem Python com pouco mais de 17 mil arquivos ocupando 81 megas. Python era uma das coisas que mais tinha biblioteca estática baixada e descompactada no meio dos projetos. De novo, eu limpei o que deu, mas provavelmente uma boa parte disso é sujeira que eu não achei tudo. Ruby é de fato mais aplicações Rails mesmo. Mas Python a grande maioria são scripts, incluindo funções pra AWS Lambda pra ficar processando dados.</p>
<p>A conclusão é que foi uma pequena startup que começou com Rails, fez muito sucesso e foi adicionando scripts em Python, microserviços e ferramentas em Go, migrando de JQuery pra Ember pra React. E pra deploys começaram  com Elastic Beanstalk e Cloud Formation e foram migrando pra Terraform, integrando com tudo que tem na AWS, evitando reinventar a roda. Em resumo é o que posso dizer sem correr o risco de dar spoilers de confidencialidade do código fonte.</p>
<p>Pra você que é iniciante e achou esse volume de código grande, isso não é grande. Lembrando que pela data dos commits dos projetos, menos da metade tá realmente ativo hoje e muita coisa é projeto que foi ficando obsoleto e abandonado pelo caminho. Metade não recebem novos commits desde 2019. E isso é outra coisa normal: muito código vai ficando obsoleto, sendo substituído por projetos novos. Lembram como eu falei em videos anteriores que um software nunca tá 100% acabado? É isso que eu quero dizer. 2 repositórios viram 10, 10 viram 100, 100 viram 1000 e depois de 5 a 10 anos você acaba com 5 mil repositórios ocupando 100 gigas, sem contar os bancos de dados e tudo mais.</p>
<p>Eu não tenho muito a criticar no que eu vi. A única coisa foi que eles precisam melhorar o gerenciamento de pacotes e dependências. Mesmo tendo muita coisa automatizada, ainda deu pra achar muita coisa realmente estática. Isso são bombas relógio que se ninguém fizer auditoria uma hora vai estourar buracos de segurança e bugs inesperados por conta de bibliotecas estáticas velhas que ninguém sabia que ainda existiam. Vai saber se foi por conta de uma dessas coisas que deu o vazamento de agora.</p>
<p>A idéia do video de hoje não era expor o Twitch. Centenas de engenheiros trabalharam sério nisso por 10 anos. Esse trabalho merece respeito, são nossos colegas da nossa área. Sair expondo o trabalho dos outros não é profissional. Meu objetivo foi totalmente educacional pra dar uma noção de dimensão pra quem nunca viu. Mesmo porque o que todo mundo teve acesso nesse vazamento é no mínimo incompleto.</p>
<p>Eu espero que vocês tenham tido alguns insights com esse caso e que sirva pra dar perspectiva nos projetos que estão trabalhando hoje. Ninguém tem que tentar fazer nada igual a um Twitch da vida, nem uma fração de 1% dos projetos que existem hoje vai chegar a ter o mesmo nível de audiência massiva que eles tem. Cada software deve ser feito pra atender as necessidades de hoje e ir evoluindo de acordo com seu sucesso. Se ficaram com dúvidas mandem nos comentários abaixo e avisando de novo que links indevidos sendo divulgados serão sumariamente banidos. Não deixem de assinar o canal e compartilhar o video pra ajudar o canal. A gente se vê, até mais.</p>
]]></content:encoded><category>seguranca</category><category>bancos-de-dados</category><category>arquitetura-de-software</category><category>akitando</category></item><item><title>[Akitando] #106 - Recomendação de Livros - Introdução à Design Emergente</title><link>https://akitaonrails.github.io/2021/10/11/akitando-106-recomendacao-de-livros-introducao-a-design-emergente/</link><guid isPermaLink="true">https://akitaonrails.github.io/2021/10/11/akitando-106-recomendacao-de-livros-introducao-a-design-emergente/</guid><pubDate>Mon, 11 Oct 2021 12:31:00 -0300</pubDate><description>&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/uDqgcsK8Kbc"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2&gt;DESCRIÇÃO&lt;span class="hx:absolute hx:-mt-20" id="descrição"&gt;&lt;/span&gt;
&lt;a href="#descri%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Toda hora alguém me pergunta &amp;ldquo;quais livros eu devo ler?&amp;rdquo; Normalmente eu não respondo, mas hoje resolvi fazer uma primeira lista de livros e temas que eu acho relevantes pra maioria dos programadores. Não é uma lista completa mas eu quero aproveitar pra tocar num tema que você eventualmente vai precisar entender: profissionalismo na área de programação e principalmente entender o que é &amp;ldquo;Design Emergente&amp;rdquo; em software.&lt;/p&gt;</description><content:encoded><![CDATA[

<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/uDqgcsK8Kbc"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<h2>DESCRIÇÃO<span class="hx:absolute hx:-mt-20" id="descrição"></span>
    <a href="#descri%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Toda hora alguém me pergunta &ldquo;quais livros eu devo ler?&rdquo; Normalmente eu não respondo, mas hoje resolvi fazer uma primeira lista de livros e temas que eu acho relevantes pra maioria dos programadores. Não é uma lista completa mas eu quero aproveitar pra tocar num tema que você eventualmente vai precisar entender: profissionalismo na área de programação e principalmente entender o que é &ldquo;Design Emergente&rdquo; em software.</p>
<p>Meio que é uma continuação ao que eu disse no meu video sobre &ldquo;Empreendendo em Software do Jeito Errado&rdquo; quando eu disse que não dá pra especificar todo um software antes de codificar e hoje vou começar a explicar porque.</p>
<h3>Conteúdo<span class="hx:absolute hx:-mt-20" id="conteúdo"></span>
    <a href="#conte%c3%bado" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li>00:00 - Intro</li>
<li>01:47 - Introduction to Algorithms</li>
<li>04:31 - The Art of Computer Programming</li>
<li>08:57 - Compilers</li>
<li>11:32 - The Design and Evolution of C++</li>
<li>12:56 - Object Oriented Analysis and Design</li>
<li>14:43 - The Unified Software Development Process</li>
<li>18:21 - UML Distilled</li>
<li>19:05 - Refactoring</li>
<li>19:33 - Agile</li>
<li>23:49 - Extreme Programming Explained</li>
<li>27:32 - Clean Code</li>
<li>28:24 - The Clean Coder</li>
<li>28:54 - The Pragmatic Programmer</li>
<li>30:33 - Design Patterns</li>
<li>33:02 - Patterns of Enterprise Application Patterns</li>
<li>33:36 - Domain Driven Design</li>
<li>35:10 - The Lean Startup</li>
<li>36:59 - The Mythical Man-Month</li>
</ul>
<h2>SCRIPT<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>Desde que abri o canal, uma das perguntas mais frequentes, tipo que eu recebo no mínimo uma vez por semana é sobre quais cursos eu recomendo pra quem tá começando. E como eu já disse em vários videos: eu não recomendo nenhum, e na prática tanto faz qual. Nenhum curso vai ser completo, eles servem só pra ter dar um empurrão, mas nunca vão te ensinar tudo de qualquer assunto. A segunda pergunta que mais recebo complementa a primeira que é quais livros eu recomendo.</p>
<p>Muito do que você não vai aprender via cursos ou tutoriais está em livros. E não ache que dá pra pegar essa lista e sair lendo tudo de uma vez. Boa parte deles você vai lendo aos poucos ao longo dos seus primeiros 10 anos de carreira, porque você ainda não tem nem o conhecimento e nem a experiência pra entender de fato o que estão tentando te dizer.</p>
<p>Hoje vou listar alguns livros que eu pessoalmente acho importantes. Não é uma lista nem completa e nem definitiva, são livros que eu fui usando ao longo dos anos e que acho que podem te ajudar. E vou apresentar mais ou menos na ordem que conheci eles, cronologicamente, porque faz diferença quando eles foram lançados e em qual contexto. Prestem atenção ao contexto que vou falar mais do que na lista em si.</p>
<p>(&hellip;)</p>
<p>Vamos lá. Nenhum livro é obrigatório. Então não saiam por aí dizendo pros outros que o Akita falou então é pra ler. Os livros que vou falar hoje eu não li tudo de uma vez só, muitos nem li inteiro, e vim acumulando ao longo dos primeiros 10 a 15 anos da minha carreira, durante e depois da faculdade.</p>
<p>Ninguém vai conseguir ler e principalmente entender esses livros numa só leitura. Isso não é literatura, são livros de aprendizado e referência. Alguns temas dentro desses livros você só vai entender de verdade depois que passar por projetos reais e só daí vai vir o &ldquo;ahhhh, era isso que o livro queria dizer&rdquo;, aquele momento &ldquo;ahá&rdquo;.</p>
<p>Isso dito, se você assiste meu canal já deve imaginar que minha primeira recomendação é o livro do Cormen de Introdução a Algoritmos. Eu tenho esta terceira edição, não sei se tem mais novo, mas não importa. O maior problema de começar estudando e usando frameworks e bibliotecas pré-prontas é que elas evitam que você precise saber como são feitos, porque já vem tudo pré-pronto e escondido pra só usar.</p>
<p>Eu falei disso em videos anteriores e até mostrei, mas se usa uma lista ou collection em Javascript, você não tem idéia se por baixo tá implementado uma lista ligada, ou uma árvore Red Black ou o que. Você só adiciona elementos, remove elementos, manda ordenar e pára por aí. E isso vale pra qualquer outra coisa funcionalidade. Você usa uma biblioteca de caching do seu framework favorito, mas como é implementado? É uma fila simples? É uma fila circular? É uma árvore B-Tree? Que tipo de hashing ele usa? Pra você tanto faz, você só empilha e desempilha elementos e deixa o framework cuidar de tudo pra você.</p>
<p>Então toda vez que usa listas, manipula strings, usa threads ou fibers, caching e tudo mais, só confia que o framework sabe o que tá fazendo e não se preocupa. E isso é ok pra 80% dos casos, por isso que muito iniciante consegue ir até longe em alguns projetos sem nunca ter lido um único livro sobre algoritmos e estruturas de dados. Esse é o nível de um junior. Porém um sênior precisa lidar com problemas diferentes.</p>
<p>Otimização de performance, otimização de carga, reorganização e refatoração do código pra ser ao mesmo tempo mais fácil de dar manutenção e também mais performático, diminuir tempo de latência, diminuir uso de memória por requisição, resolver conflitos de versões de bibliotecas externas, fechar buracos de segurança e mais. E pra isso, você precisa saber como essas bibliotecas e frameworks funcionam por baixo, e pra isso, precisa da fundação, que começa em algoritmos e estruturas de dados.</p>
<p>Um livro como do Cormen não é completo. Ele fala de bastante coisa que você deveria saber, mas nenhum livro nunca vai ter tudo. E antes que vocês perguntem nos comentários ou fiquem me mandando DMs, não, não precisa ser o livro do Cormen. Eu mesmo não usei ele na faculdade. Eu aprendi o básico com um muito mais simples, este do Niklaus Wirth. Olha como é menor.</p>
<p>Diferente do Cormen, o livro do Wirth sequer entra no assunto de grafos, por exemplo. Além disso o Wirth escreveu o livro dele em 1986, já o Cormen e seus co-autores escreveram o deles em 2009. Por isso, no livro mais novo tem assuntos mais modernos, como multithreading, que ainda era um assunto que engatinhava nos anos 80.</p>
<p>Se você acha que precisa ter o livro mais completo de todos, ele não existe mas o mais próximo não é um livro, mas uma coleção de livros, do lendário Donald Knuth, o famoso The Art of Computer Programming, o magnum opus da ciência da computação que ele começou a escrever em 1962 e até hoje não terminou. Eu já falei dele em outro video mas os livros que eu tenho são do 1 ao 3 mais o 4A. Segundo a Wikipedia ele tá escrevendo os fascículos 5 e 6 que são os primeiros dois terços do volume 4B.</p>
<p>E quanto ainda falta pra acabar? Bom, depois do 4B ainda vai ter os volumes 4C e 4D e depois os volumes 5, 6 e 7. Ou seja, o que eu tenho aqui ainda não é nem metade do que seria a obra completa. Essa coleção é o Game of Thrones da computação, e o Knuth é o George R R Martin da nossa área. Todos cruzando os dedos pra ele conseguir terminar tudo. Os livros do Wirth e do Cormen eu diria que são um resumão do que seria os livros de 1 a 4D do Knuth.</p>
<p>Ou seja, os temas dos primeiros 7 volumes do Knuth, que incluem os volumes 4B a 4D que ele ainda não escreveu são o que você encontra no sillabus dos primeiros anos de um curso de ciência da computação. Começa com coisas como matemática básica, números, potências, logaritmos, teoria dos números, permutações, fatoriais, coeficientes binomiais, ou seja, não só o assunto de algoritmos mas temas de matemática que você aprende nos primeiros 2 anos de ciências da computação em matérias como Álgebra, Estatística e probabilidade.</p>
<p>No primeiro volume do Knuth você tem coisas básicas como pilhas, filas, listas ligadas, listas circulares, arrays, árvores binárias, que foram assuntos que eu detalhei alguns videos atrás. Por isso o primeiro volume se chama Algoritmos Fundamentais. Já o segundo volume complica um pouco mais. Ele se chama Algoritmos Seminuméricos e logo de cara o primeiro assunto no livro é definir números aleatórios.</p>
<p>Fazendo uma tangente, números aleatórios gerados por computador não são puramente aleatórios, são pseudo-aleatórios. Em qualquer linguagem moderna de programação você só usa uma função como Random e esquece, mas nesse livro você pode estudar como isso funciona. E nos capítulos seguintes ele vai discorrer sobre sistemas posicionais de números como aritmética de ponto flutuante, que foi outro assunto que já expliquei alguns videos atrás. E continua em assuntos como aritmética racional, aritmética polinomial.</p>
<p>E sim, essa coleção inteira é bem pesada em notação matemática. Se você não tá acostumado é absolutamente intimidador. Entendida toda a base matemática fundamental, só no volume 3, que é chamado de Ordenação e Procuram, é que vamos finalmente encontrar os algoritmos de ordenação como inserção, seleção e procura com temas como pesquisa binária e árvores balanceadas que eu também já mostrei alguns videos atrás.</p>
<p>Finalmente chegamos ao volume 4A que é chamado de Algoritmos de Combinatória Parte 1 que explica o básico de álgebra booleana, truques e técnicas de operações bitwise, diagramas de decisão binária e geração de todas as possibilidades onde entramos no assunto de n-tuplas, permutações, combinações, partições, conjuntos e muito mais. E isso é parte 1 porque o tema de Algoritmos de Combinatória vai se esticar nos próximos volumes ainda não escritos, do 4B ao 4D.</p>
<p>Só esses 4 volumes somam quase 3 mil páginas. Pra colocar em perspectiva, o livro do Cormen é menos da metade disso. Mas se prestou atenção vai notar que os livros do Knuth cobrem mais assuntos, em particular a base matemática. Livros mais focados só em algoritmos e estruturas de dados, como do Cormen, se preocupam menos com as provas matemáticas e mais em como cada elemento é implementado e funciona. Eu diria que é mais prático pra gente. Você só vai realmente ler página a página do Knuth se for se especializar em matemática aplicada ou algo assim.</p>
<p>Mas pra assuntos específicos, vale como referência se quiser descer a fundo. Por exemplo, como realmente números aleatórios são matematicamente definidos? Eu repito, a grande maioria de nós não tem o vocabulário de matemática teórica suficiente pra realmente conseguir absorver tudo do Knuth, por isso qualquer coisa perto do livro do Cormen é mais prático. Mesmo o Cormen é um pouco pesado em matemática, então qualquer livro de algoritmos e estruturas de dados é suficiente.</p>
<p>Mas e os livros de 5 a 7 que o Knuth ainda não escreveu? Os títulos parecem que vão ser Algoritmos Sintáticos, teoria de linguagens livres de contexto e técnicas de compiladores. Linguagens livres de contexto você vai cair no trabalho mais importante do Noam Chomsky - e na minha opinião, a única coisa que você precisa ler dele. Enfim, na realidade já existem livros que cobrem esses assuntos. Se você fez ciência da computação já sabe. Essa é minha segunda recomendação: o famoso livro do dragão, o Compiladores - Princípios, Técnicas e Ferramentas, do Aho, Ullman e outros, publicado em 1986.</p>
<p>Eu já falei em outros episódios sobre compiladores como GCC, e principalmente sobre uma das tecnologias mais importantes deste século: o LLVM, que se você pelo menos acompanha artigos técnicos, já ouviu falar diversas vezes. Eu explico sobre LLVM no episódio sobre a Apple e recomendo que assistam pra entender o contexto, mas é graças a LLVM que você tem linguagens como Swift, Rust, Crystal, Julia, linguagens de shaders pra GPU e muito mais. E pra entender LLVM você precisa entender compiladores e realmente como linguagens de programação são criadas.</p>
<p>Na prática, a menos que você pretenda criar sua própria linguagem do zero, não precisa saber o livro do Dragão de cabo a rabo. É mais pra entender os principais conceitos. Esse livro vai te explicar o que é análise léxica e ferramentas como Lex e você mesmo vai descobrir outras como o GNU Flex depois. Daí vai pular pra análise sintática e ferramentas como o Yacc e outros como o GNU Bison. O livro usa os antigos Lex e Yacc, na minha época da faculdade no meio dos anos 90 era Flex e Bison, mas hoje em dia existem ferramentas mais modernas.</p>
<p>Isso é só pra desenhar a linguagem e criar um parser, mas a partir daí tem que fazer a geração do código binário propriamente dito que passa por um código intermediário, otimizações independentes de máquina, paralelismo em nível de instrução, localidade, análise interprocedural. Se você me perguntar se eu ainda sei essas coisas, não, não lembro nem uma fração mais hoje. Mas os conceitos que guardei me ajudam a avaliar toda vez que uma nova linguagem aparece, eu consigo entender como funciona, o que procurar na linguagem, no compilador, no código final gerado e tudo mais.</p>
<p>Compiladores é um tema que você não deveria tentar aprender tudo de uma só vez. Compensa até começar com algum livro mais simples, curso online ou até um tutorial com ferramentas de mais alto nível que te permitem criar linguagens simples que não necessariamente vão ser performáticas, mas vão ajudar a &ldquo;molhar os dedos&rdquo; no assunto antes de aprofundar nos detalhes que tem no livro do dragão.</p>
<p>De qualquer forma, só faz sentido ter a ambição de escrever a própria linguagem se você estudou um pouco mais a fundo sobre linguagens. E não só &ldquo;ah, eu gosto de parênteses&rdquo; ou &ldquo;ah, eu prefiro não ter ponto e vírgula no fim das linhas&rdquo;. Design de linguagem é muito mais que isso. Um livro que talvez ajude a entender as features de uma linguagem é o The Design and Evolution of C++, do Bjarne Stroustrup que é o pai do C++.</p>
<p>Ele conta um pouco a história de como o Stroustrup se inspirou em C with Classes pra chegar ao C++ e justifica as funcionalidades que criou em cima disso como o gerenciamento de memória, herança múltipla, casting, templates, exceptions, namespaces e tudo mais que forma a base do C++ e depois serviu de inspiração pra implementar muitas das linguagens modernas que você usa como um Java ou C#.</p>
<p>Entender o pulo do C pro C++ pela visão do autor é interessante e também as primeiras interpretações sobre o que é orientação a objetos. C++ é uma linguagem que eu mesmo nunca usei muito profissionalmente, então eu mesmo sou amador, mas acho fascinante entender como se encaixa na história das linguagens de programação. Ela ainda é uma das linguagens mais usadas pra código de sistemas, tudo de mais importante que você usa é escrito com C e C++, seu Javascript é escrito em C++. E isso não vai mudar por um bom tempo ainda.</p>
<p>Daí pulamos um pouco do tema de ciências da computação e vamos um pouco pra engenharia de software. Eu já expliquei a diferença em outro video, mas aproveitando o gancho da orientação a objetos do C++ vale pelo menos mencionar outro livro, o Object Oriented Analysis and Design with Applications do Booch e outros autores.</p>
<p>Orientação a objetos é um tema super longo, bem mais complexo do que você aprende na faculdade e eu não vou detalhar tudo hoje. Dependendo da era que estudar você vai ter interpretações diferentes de muitos conceitos, mas o que muita gente chama de orientação a objetos deriva do vocabulário criado pelo Grady Booch, Ivar Jacobson e Jim Rumbaugh. Cada um deles tinha uma versão de definição de orientação a objetos e eles se juntaram pra compilar seus trabalhos numa obra só. O que se publicou dos anos 90 em diante deriva disso.</p>
<p>Esse livro em particular não é a obra definitiva nem nada disso, mas eu acho interessante que ele tenta justamente definir muito desse vocabulário, definir o que exatamente é uma classe, o que é um objeto, qual é a notação que se deveria usar pra comunicar um modelo de objetos. Algumas partes, em particular a seção 3 do livro que pode estar um pouco defasado porque ele tenta explicar esses conceitos usando exemplos de cinco aplicações que vão de um sistema de navegação de satélite até um sistema de rastreio de férias via web, definido com essa notação de objetos.</p>
<p>Eu digo defasado porque a forma como desenvolvemos aplicações e até a forma como modelamos classes e objetos mudou daquela época até hoje então alguma coisa ficou obsoleta mesmo, não tem jeito, mas muitos dos conceitos ainda valem hoje e principalmente o raciocínio e história por trás de porque eles definiram as coisas daquele jeito. Esse livro em particular vale mais pra capturar um momento da história do que pra ensinar todas as técnicas que você precisa saber.</p>
<p>Esse livro, na real, é opcional e complementar a outro livro mais conhecido que é o The Unified Software Development Process escrito pelos mesmos Los 3 Hermanos, o Jacobson, Booch e Rumbaugh. Ele define a famosa e famigerada notação UML mas, mais importante, define o processo de desenvolvimento de software conhecido como Unified Process ou até hoje o que você conhece como Rational Unified Process ou RUP.</p>
<p>Lembrando, esses livros foram escritos antes de 2001, nos anos 90, portanto antes do Manifesto Ágil, antes de cloud computing, antes de smartphones, antes ainda da primeira bolha da internet, literalmente na infância da Web. Então tudo que você conhece como formas modernas de desenvolvimento de software, que só nasceram nos últimos 20 anos, ainda não existiam nessa época. Mesmo assim, eu ainda acho que vale a pena ler o começo desse livro, porque muitos dos conceitos de engenharia de software que você usa são derivados dessa época.</p>
<p>Em particular, eu já falei em outros episódios que pra mim um bom exemplo de linguagem orientada a objetos, por incrível que pareça é o Erlang, em particular o Elixir. Muita gente pensa em Elixir como uma linguagem funcional, mas pra mim é o contrário. Ele é primeiro uma linguagem orientada a objetos que por acaso tem aspectos funcionais. Eu argumentaria que Elixir e Erlang são mais orientados a objetos do que Java, que muita gente ainda pensa como a referência em objetos. Se você não entendeu isso, ainda precisa estudar mais.</p>
<p>Um dos argumentos sobre Erlang é que logo no prefácio desse livro, eles contam sobre o case da Ericsson, que é a inspiração do Unified Process. Se você não sabe, o “ER” de Erlang é de Ericsson. Os autores argumentam que o sistema de telecomunicações deles era tão bom porque modelaram o sistema inteiro como um conjunto de blocos interconectados, o que em UML se chamaria de componentes ou sub-sistemas. E eles foram montando blocos de baixo nível e subindo pra sub-sistemas de alto nível pra tornar o sistema mais fácil de gerenciar e foram encontrando esses blocos trabalhando pelos casos de tráfego anteriores - o que hoje se chamaria em UML de &ldquo;casos de uso&rdquo;.</p>
<p>O caso da Ericsson é relevante pro assunto de orientação a objetos porque quem trabalhou lá foi justamente um dos co-autores desse livro, o Jacobson, que saiu em 1987 e fundou a empresa Objectory e nos anos seguintes ele e alguns colaboradores desenvolveram um processo chamado Objectory também pra servir pra mais casos além de telecomunicações, e foi aí que coisas como &ldquo;casos de uso&rdquo; ganharam esse nome.</p>
<p>Alguns anos depois, em 1995 a empresa Rational Software Corporation comprou a Objectory e eles trabalharam em juntar os dois processos que tinham e chegaram primeiro no Rational Objectory Process. Depois disso que o Booch, Rumbaugh e Jacobson colaboraram pra criar o Unified Modeling Language, o UML, pra ter uma linguagem visual pra comunicar as idéias sobre objetos e finalmente chegaram no Unified Process ou Rational Unified Process, que é o RUP, que muitos que fizeram engenharia de software devem ter estudado.</p>
<p>Em 2021 raramente você vai ver alguma empresa usando fortemente UML e RUP em projetos. Pelo menos eu espero que não. Talvez em empresas antiquadas e altamente burocráticas, mas em tech startups é bem raro encontrar. Mas muito do vocabulário e notações que usamos até em rascunhos numa lousa são derivados do UML. Muito da engenharia de software moderna ainda usa os nomes originais, como &ldquo;casos de uso&rdquo; por exemplo, ou diagrama de componentes. O que eles chamavam de sub-sistemas você poderia chamar hoje de micro-serviços e assim vai.</p>
<p>Se quiser aprender esse vocabulário sem perder muito tempo no detalhamento do processo em si, que ninguém mais usa como antigamente, você pode pular direto pra outro livro bem menor, de menos de 200 páginas, chamado UML Distilled do Martin Fowler, que é basicamente um resumão de UML e RUP. Ele tem só o que o Fowler acha que é mais relevante alguém saber, é quase um cheat sheet sobre UML.</p>
<p>Levou alguns anos, até demais na minha opinião, pras pessoas entenderem que seguir algo como um RUP ao pé da letra era absolutamente burocrático e improdutivo. A gente tava mais preocupado em rastrear, gerenciar e aprovar artefatos, como diagramas, do que com o código em si. É uma das grandes críticas em relação ao campo da orientação a objetos em geral.</p>
<p>Falando em Martin Fowler, ele foi um dos escritores mais prolixos e influentes do fim dos anos 90 até pelo menos o meio dos anos 2000 e um dos seus livros mais importantes parece uma reação contra o que um RUP representa. Esse livro obviamente é o Refactoring, publicado em 1999. E hoje em dia todo mundo fala em refatorar, refatorar, mas quantos de vocês conseguem definir o que é refatorar? Qual a diferença entre refatorar e reescrever?</p>
<p>Nas palavras do próprio Fowler, refatorar é o processo de mudar um software de tal maneira que isso não altere o comportamento externo do código e mesmo assim melhore a estrutura interna. É uma forma &ldquo;disciplinada&rdquo; de limpar código que minimiza as chances de introduzir bugs. Em essência, quando você refatora, está melhorando o design do código depois que ele foi escrito. Muita gente falando “refatoração” na verdade está querendo dizer “reescrever”, que não é um processo disciplinado de melhoria sem mudar comportamento, é literalmente jogar fora e escrever de novo de outro jeito, com outro comportamento, e não garante nenhuma melhoria.</p>
<p>E não é à toa que o Martin, junto com outros autores como Kent Beck, são signatários do Manifesto Ágil. Em metodologias antigas de engenharia de software, onde o RUP se inclui, a ideia principal é primeiro criar o design inteiro do software, por exemplo desenhando com diagramas com UML. E só depois que o design estiver perfeito que se começa a escrever o código. Ou até mesmo daria pra gerar automaticamente esse código a partir dos diagramas. Eles consideram design e codificação como duas etapas separadas. Mesmo tendo coisas como iteração no processo, as etapas em si ainda são conceitualmente separadas. Muitas empresas tentaram criar ferramentas que partiam dos diagramas de classes e tudo mais de UML e geravam código em Java da vida.</p>
<p>Essa ideia nunca funcionou direito e a gente foi entendendo melhor porque depois da virada do século, depois que começamos a ideia de agilidade. A premissa errada é achar que é possível criar um design perfeito em papel primeiro e depois o código seria só um reflexo direto desse design. Na realidade o código &ldquo;é&rdquo; o design e ele nunca vai estar perfeito, muito menos na primeira vez. Daí vem a ideia de fazer o primeiro código que funciona primeiro, que provavelmente vai ser um código ruim, e só depois, disciplinadamente, refatorar esse código até termos uma estrutura que seja livre de bugs e de mais fácil manutenção.</p>
<p>Aliás, vale aproveitar pra dizer que muita gente pensa que primeiro nasceu o Manifesto Ágil em 2001 e só depois começamos a entender o que são técnicas ágeis, mas é o contrário. Primeiro os signatários do manifesto passaram anos praticando, formulando as técnicas e documentando, durante os anos 80 e 90 principalmente, e só depois que isso deu origem ao Manifesto. Essa ordem é importante, porque o Manifesto é só um resuminho de 20 anos de experiência de cada um deles que veio antes.</p>
<p>Na indústria mais tradicional, por décadas sempre se tentou o conceito de design primeiro e código depois. Lá atrás a gente desenhava software com fluxogramas em linguagens procedurais, por exemplo. E isso fazia sentido porque debugar software depois era custoso, difícil, tedioso. As linguagens antigas eram fortran, cobol, algol, Ada ou C. Nenhum programador nos anos 70 e antes tinha um notebook multi-core com gigabytes de RAM. Não, eles tinham que compartilhar um único computador grande com vários outros engenheiros e programar a partir de terminais remotos.</p>
<p>Esses terminais eram lentos, ninguém tinha um VS Code ou Sublime Text da vida. O editor VI original foi criado justamente pra ser super otimizado pra desenhar o mínimo possível na tela e permitir navegar no código com o mínimo de comandos. Porque cada comando navegava numa rede lenta até um computador compartilhado lento que tinha no máximo kilobytes de RAM. Portanto, você gostaria que o código funcionasse de primeira, não era produtivo ficar debugando e &ldquo;refatorando&rdquo; o código depois.</p>
<p>O que aconteceu nos anos 90 até os 2000 é que microcomputadores apareceram e ficaram consideravelmente potentes, internet e rede mais veloz apareceu, e agora era factível você rodar um sistema inteiro no seu computador pessoal antes de rodar no servidor da empresa. Fazer o design antecipadamente era crucial nas décadas de 60 a 80, mas depois disso a tecnologia avançou o suficiente pra gente conseguir fazer código ruim primeiro e refatorar depois pra ter código bom, então o design e o código passaram a ser um processo orgânico de melhoria contínua. Isso é uma interpretação minha, mas eu diria que “Agile” marca quando separávamos design e código em etapas distintas pra serem unidos em design emergente.</p>
<p>Falando em processos ágeis, se tiver só um livro que você queira ler, que seja o Extreme Programming Explained, do Kent Beck, também de 1999. Veja que em 1999 e 2000 que começou a aparecer mais livros sobre esse novo jeito de programar que vinha sendo praticado fazia mais de 10 anos, que culminaria com o manifesto ágil em 2001 e muito do que você considera técnicas de engenharia de software modernas como pair programming, test driven development, continuous integration, refactoring, small releases, nascem nele.</p>
<p>Engenharia de software é um campo em constante mudança. Dependendo de que ano é a literatura que você está estudando, vai cair nos RUP da vida ou vai cair em derivados de Extreme Programming. A ideia não é dizer que os conceitos dos anos 90 estavam errados. Eles faziam sentido pras circunstâncias da época e vale a pena entender porque.</p>
<p>Vocês podem ver que alguns livros e temas envelhecem melhor que outros. Uma coisa que me ocorreu gravando esse video é que se você prestou atenção, note que livros de ciência da computação tendem a envelhecer melhor que livros de engenharia de software. Livros de ciência da computação de 30 anos atrás ainda se aproveitam hoje, mas livros de engenharia de software de 30 anos atrás, estão obsoletos.</p>
<p>Até hoje ainda não temos &ldquo;o&rdquo; melhor jeito de escrever software, estamos constantemente mudando os processos de como transformar idéias em código. Mas as peças fundamentais que usamos pra construir essas idéias ainda continuam válidas. Na ciência da computação o conhecimento meio que vai se estendendo mas não jogando muita coisa fora pelo caminho.</p>
<p>Uma parte disso é porque as teorias matemáticas que embasam coisas como algoritmos, uma vez provadas, variam muito pouco na implementação. É o trabalho de caras como Knuth. Uma árvore balanceada, seja feita em C ou seja em Kotlin, continua sendo uma árvore balanceada. Linguagens diferentes mas as características continuam as mesmas. É um dos motivos de porque linguagens funcionais ganharam algum destaque nos últimos anos. Muitos tentam trazer esse rigor matemático, como de lambda calculus, pros processos de como escrever código. Mas escrever código não é uma ciência exata, é um processo aberto à interpretação.</p>
<p>O problema é que código não é só rápido, ou seguro. Em engenharia a gente tenta buscar várias características como mantenabilidade, flexibilidade, portabilidade, reusabilidade, legibilidade, testabilidade, segurança e assim por diante. Você pode escrever o código mais seguro possível, mas vai prejudicar a flexibilidade e reusabilidade, e assim por diante. O desafio de um programador é balancear essas características, e isso é difícil porque depende de quais problemas você precisa realmente resolver com esse código. E pior, os requerimentos desse código mudam conforme o software é usado.</p>
<p>Num primeiro momento a prioridade pode ser eficiência, mas depois de um tempo a prioridade passa a ser segurança, depois passa a ser mantenabilidade. Então o código precisa ser modificado ao longo do tempo, o design desse código vai mudando conforme as necessidades vão mudando. Por isso, nenhum código está 100% pronto e congelado pra sempre. E repetindo, por isso é quase impossível fazer um design perfeito antes do código.</p>
<p>Muito amador ainda acha que código, se rodar, tá ótimo, não importa se tá sujo, com nomenclaturas toscas, com alta complexidade. &ldquo;Ah, roda, então foda-se&rdquo;. Código que roda, mas é sujo, com baixa mantenabilidade, é lixo, pode jogar fora. Não adianta nada rodar se amanhã você precisar estender, consertar, reusar. Daí mais atrapalha que ajuda. É lixo. Não basta rodar,  precisa estar limpo o suficiente pra quando outro programador for reusar, não vai sofrer.</p>
<p>Se você quiser um livro mais estruturado com a melhor forma de escrever código e como se comportar como um bom profissional, o melhor livro pra começar é de outro agilista, o controverso Robert Martin, vulgo Uncle Bob. Ele é outro escritor prolixo e seu livro mais conhecido é sem dúvida o Clean Code, ou Código Limpo. E o conteúdo é parecido com o Code Complete do McConnell mas bem mais atual.</p>
<p>Ele toca em coisas tipo, como criar nomes de variáveis, funções que sejam fáceis de entender. E segue falando de todas as boas práticas que deveriam ser óbvios pra todo profissional: escrever funções com corpo curto, código bem formatado e legível, como escrever código que lida com exceções, como criar fronteiras claras entre diferentes módulos, como fazer testes unitários, um pouco sobre organização de classes em linguagens orientadas a objetos, noções de design emergente, ou seja, design que emerge à medida que você escreve código, testa e refatora.</p>
<p>Eu diria que se você quer ser um bom profissional, no mínimo tem que escrever código limpo como o Bob Martin ensina E falando em profissionalismo, um livro mais recente dele que lida com temas mais sobre como trabalhar em equipe, como gerenciar tempo, como fazer estimativas e coisas assim é o Clean Coder, ou Programador Limpo. Esse eu mesmo não cheguei a ler, mas pelo pouco que vi, acho que pode ser útil pra quem tá começando na profissão e ainda não sabe exatamente quais são suas responsabilidades perante seus colegas e projetos.</p>
<p>Outro livro com tema similar é um grande clássico que fez 20 anos recentemente, escrito por outros dois agilistas, Dave Thomas e Andy Hunt, o famoso The Pragmatic Programmer. Se o Clean Code e o Clean Coder do Bob Martin são meio que um manifesto do profissional de código, o The Pragmatic Programmer é uma mistura dos dois e tenta explicar a filosofia de um programador pragmático. Eles discorrem sobre como lidar com resolução de problemas de software, tocam em assuntos de agilidade de novo como testes, como escrever código limpo.</p>
<p>Se você assistiu meu episódio intitulado Esqueça Metodologias Ágeis, já sabe que estamos numa era pós-Agile. Os conceitos e técnicas originais de agilidade são simples, mas diversas empresas e consultorias sequestraram o termo &ldquo;ágil&rdquo; e inventaram um monte de metodologias e ferramentas burocráticas que nos levaram de volta à época do RUP e UML.</p>
<p>Se você quer saber o que é realmente ser Ágil, deve ler os autores originais e não trabalhos derivados. Em particular Martin Fowler, Kent Beck, Bob Martin, Dave Thomas e Andy Hunt, que são todos os livros que eu recomendei até agora. Leia os livros deles e você vai estar meio caminho andado pra conseguir distinguir ágil de verdade das bullshitagens de coach de consultoria ágil por aí.</p>
<p>Muitos já me pediram pra falar sobre alguns desses temas, como testes. Ainda não sei se vou fazer um video disso porque é um tema tão bem documentado já que você só não aprende se fizer esforço pra ignorar. E repetindo: leia sobre assuntos de agilidade a partir dos autores originais. Quer saber sobre testes? Comece com Kent Beck e seus livros de Extreme Programming, não tem que inventar moda.</p>
<p>Tem três outros livros que vale mencionar. Uma coisa que vocês só vão realmente entender lendo e escrevendo muito código de verdade é sobre a diferença entre design e código e como design emerge a partir do código e de seu uso, como eu já disse. Num primeiro momento, se está começando os estudos, não pense que vai conseguir pré-planejar todos os aspectos de um software antes de escrever uma linha de código. Não vai. E os próximos livros podem confundir vocês nisso.</p>
<p>O primeiro é o clássico Design Patterns, escrito pelo Erich Gamma e sua gangue dos quatro. No tema de achar que dá pra planejar em detalhes antes de codificar, esse é o primeiro livro mal interpretado. Cansei de ver amador com esse livro embaixo do braço querendo pagar de arquiteto e falando bullshitagens como &ldquo;vamos fazer esse módulo usando alguns decorators pra montar um adapter que vai sair de um factory que vai ser um singleton derivado de um command de um chain of responsability suportado por um observer&rdquo;. Bleh.</p>
<p>Eu já expliquei isso no fim do meu video sobre como aprendi inglês, mas vou repetir. A tradução do título desse livro em português é &ldquo;Padrões de Design&rdquo;. Dá a impressão que esse livro tem todos os padrões, no sentido de as melhores peças, ou as peças definitivas, pra se fazer design de software. O problema é que a palavra &ldquo;padrão&rdquo; em português pode ser traduzida em 3 palavras diferentes em inglês, standard, default ou pattern.</p>
<p>Muita gente pensa que &ldquo;padrão&rdquo; é o standard. E standard é como você pensou mesmo: um padrão oficial, uma definição definitiva sobre alguma coisa. Mas estamos falando de &ldquo;pattern&rdquo;, e pattern é só alguma coisa que se repete muito, independente se é boa ou ruim e definitivamente não é o &ldquo;padrão definitivo&rdquo;. O livro de Design Patterns é um catálogo de patterns, de repetições que os autores viram muito em diversos projetos, e simplesmente deram um nome pra eles. Só isso, nada mais, nada menos.</p>
<p>Esse livro foi publicado em 1994, um ano antes de entrar na faculdade. E eu só fui conhecer ele alguns anos depois, lá por 1998. Nesse momento eu já tava programando faz alguns anos e quando vi o livro, em inglês, foi exatamente o que pensei. &ldquo;Oras, o livro só dá nome pra coisa que já vi várias vezes&rdquo;. Por exemplo, quem mexia com emuladores naturalmente reconhecia o Design Pattern Adapter. Toda vez que troco a biblioteca opengl do game por um directx é meio que um Adapter.</p>
<p>Design Patterns é um catálogo, e não um manual de como fazer design. Se você já programou tempo suficiente, só vai descobrir que alguns trechos de código que já viu várias vezes por acaso tem nomes, como Singletons, ou Visitors. Esse livro é útil somente pra nos dar um vocabulário comum pra ficar mais fácil pra dois programadores comunicarem um conceito com um nome comum. E ele nem é o catálogo mais completo, existem diversos outros patterns que não estão nesse livro. Vide livros como Patterns of Enterprise Applications Architecture do próprio Martin Fowler.</p>
<p>Falando em arquitetura, outro livro que alguns podem usar errado pensando que é um manual de instruções é o Domain Driven Design do Eric Evans, publicado em 2003. Nas palavras do Martin Fowler, esse livro descreve uma forma de desenvolvimento de software centrado num modelo de domínio que tem um entendimento rico dos processos e regras desse domínio. Em resumo bem resumido, é mais um livro que orienta em como dar nome pras coisas e como organizar essas coisas.</p>
<p>Assim como Design Patterns, ele nos dá novos vocabulários pra descrever sistemas como Ubiquitous Language, Aggregates, Bounded Contexts e outras classificações, mas enxergando mais o big picture de um sistema em vez de só pequenos componentes. Bounded Contexts em particular é interessante porque descreve um pattern que aparece em muitos sistemas.</p>
<p>Por exemplo, imagine que você tem um sub-sistema que tem contexto de Vendas e outro sub-sistema com contexto de Suporte. No de vendas você tem entidades como Vendedor, Oportunidade. No de suporte tem entidades como ticket, defeitos. Mas no bound, na fronteira entre os dois sub-sistemas ele compartilham entidades como o usuário ou o produto.</p>
<p>Começa a ficar difícil de lidar com sistemas que crescem se num sub-sistema você chama usuário de cliente e no outro sub-sistema você chama de comprador, por exemplo. Parte da idéia de DDD é criar um linguagem ubíquita, comum entre desenvolvedores e usuários. Enfim, como eu disse antes, ele se preocupa muito com a arte de dar bons nomes pras coisas. Não é à toa que a gente brinca que uma das coisas mais difíceis da computação é dar nomes pras coisas.</p>
<p>O terceiro e último livro, por incrível que pareça acho que é a cola que une todos os livros de engenharia que falei até aqui. É o famigerado The Lean Startup, do Eric Ries, publicado em 2011. Foi exatamente quando começou a atual bolha de tech startups e esse livro apareceu na hora certa pra se tornar meio que a bíblia das novas tech startups. É um livro meio que de empreendedorismo, é superficial, e por causa disso muita gente interpreta do jeito errado. Próxima pessoa que falar que “validou” a idéia fazendo pesquisa com uma dúzia de pessoas, eu juro que eu chuto.</p>
<p>De qualquer forma, ele fala algumas coisas que são relevantes pra engenharia de software. Você pode estudar orientação a objetos, processos de desenvolvimento de software, metodologias ágeis, como fazer código limpo, como modelar sistemas com DDD, mas se você não entende o mínimo de negócios, seu software sempre vai ser ruim. Porque o melhor software é aquele que resolve problemas do mundo real, e não do mundo teórico.</p>
<p>Um conceito importante que o The Lean Startup ajudou a popularizar foi o de Minimum Viable Product ou MVP, ou simplesmente Protótipo. A ideia ultrapassada, vinda de processos monumentais como RUP da vida, é de tentar fazer o design do melhor software possível, o mais complicado, com mais opções, com maior número de funcionalidades possíveis. Eu não sei se alguém aqui já passou por isso, mas eu já tive que avaliar calhamaços, literalmente pilhas de quinhentas páginas de especificações e diagramas de UML.</p>
<p>Nunca vi um design assim no papel que desse pra traduzir em software, sempre tinha um monte de buracos pelo caminho. É impossível desenhar 100% de um software antes de construir, é sempre mais prático começar com um design mínimo, construir, aprender com os erros e ir estendendo e refatorando, ou seja, fazendo o design emergir do software.</p>
<p>Casos que já vi várias vezes, seja a partir de um software legado já existente ou um MVP, povo sempre fica com a ideia de &ldquo;agora sim, eu já sei como que o design de verdade deveria ser&rdquo; e aí segue tentando desenhar a versão 2.0 &ldquo;perfeita&rdquo;, e cai na armadilha do &ldquo;Segundo Sistema&rdquo; que nosso amigo Fred Brooks já avisou no livro The Mythical Man-Month, que eu já fiz um video inteiro a respeito meses atrás. Dêem uma olhada.</p>
<p>É muito difícil fazer uma lista com TODOS os livros que eu acho que todo mundo deveria ler. Acho que uma lista assim seria impossível. Dependendo da sua escolha de carreira ou mesmo dos seus gostos, alguns são mais interessantes que outros. Essa lista é a que pessoalmente acho que é relevante, embora incompleta, pra maioria. Minha recomendação é sempre ir atrás dos autores originais de um determinado tema, autores que têm mais de décadas de experiência em projetos reconhecidos. Evitar ao máximo derivativos que costumam simplificar demais ou até alterar o que o original dizia.</p>
<p>Mas como eu disse antes, em particular livros de engenharia de software tendem a envelhecer mais rápido do que livros técnicos de ciências da computação. Mas ambos os temas são importantes pra qualquer programador. De qualquer forma, se ficaram com dúvidas mande nos comentários abaixo, se curtiram o video deixem um joinha, não deixem de assinar o canal e compartilhar com seus amigos. A gente se vê, até mais!</p>
<p>06:52 - o que vem depois hello world
09:50 - Apple GPL</p>
]]></content:encoded><category>arquitetura-de-software</category><category>engenharia-de-software</category><category>aprendizado</category><category>akitando</category></item><item><title>[Akitando] #49 - Devo usar NOSQL? | "ENDGAME" para Iniciantes em Programação | Série "Começando aos 40"</title><link>https://akitaonrails.github.io/2019/04/24/akitando-49-devo-usar-nosql-endgame-para-iniciantes-em-programacao-serie-comecando-aos-40/</link><guid isPermaLink="true">https://akitaonrails.github.io/2019/04/24/akitando-49-devo-usar-nosql-endgame-para-iniciantes-em-programacao-serie-comecando-aos-40/</guid><pubDate>Wed, 24 Apr 2019 17:00:00 -0300</pubDate><description>&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/EdOkYEE1J_Y"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Finalmente chegamos ao décimo-quarto e último episódio da série &amp;ldquo;Começando aos 40&amp;rdquo;. E eu quis coincidir o término da série com o dia da pré-estreia de Avengers: Endgame, porque eu sou geek assim mesmo :-)&lt;/p&gt;
&lt;p&gt;No episódio de hoje vou dar um pequeno overview sobre o que eu considero como alguns dos principais aspectos de bancos de dados relacionais, NoSQL, e as melhores escolhas pra sua aplicação.&lt;/p&gt;</description><content:encoded><![CDATA[

<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/EdOkYEE1J_Y"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>Finalmente chegamos ao décimo-quarto e último episódio da série &ldquo;Começando aos 40&rdquo;. E eu quis coincidir o término da série com o dia da pré-estreia de Avengers: Endgame, porque eu sou geek assim mesmo :-)</p>
<p>No episódio de hoje vou dar um pequeno overview sobre o que eu considero como alguns dos principais aspectos de bancos de dados relacionais, NoSQL, e as melhores escolhas pra sua aplicação.</p>
<p>Vou aproveitar pra complementar a discussão de máquinas virtuais, containers, IaaS, PaaS adicionando minhas opiniões sobre FaaS ou Function as a Service.</p>
<p>E no final quero deixar um pensamento sobre esse mundo cloud e o impacto quanto ao mundo de código livre pra todos pensarem.</p>
<p>Se você ainda não assistiu todos os episódios, não esqueçam que tem a playlist com todos os episódios pra maratonar: <a href="https://www.youtube.com/watch?v=O76ZfAIEukE&amp;list=PLdsnXVqbHDUc7htGFobbZoNen3r_wm3ki"target="_blank" rel="noopener">https://www.youtube.com/watch?v=O76ZfAIEukE&list=PLdsnXVqbHDUc7htGFobbZoNen3r_wm3ki</a></p>
<p>Desta vez quero tirar uma folga de uma ou duas semanas antes de retornar então é o melhor momento pra ver todos os vídeos do canal que ainda não assistiu.</p>
<p>Ajudem o canal compartilhando pra todo mundo!</p>
<p>Pular direto pra FaaS: 20:56</p>
<p>Pular direto pra Conclusão: 26:46</p>
<p>Links:</p>
<ul>
<li>
<p>Was MongoDB Ever the Right Choice? (<a href="https://www.simplethread.com/was-mongodb-ever-the-right-choice/"target="_blank" rel="noopener">https://www.simplethread.com/was-mongodb-ever-the-right-choice/</a>)</p>
</li>
<li>
<p>Redis Labs changes its open-source license - again (<a href="https://techcrunch.com/2019/02/21/redis-labs-changes-its-open-source-license-again/"target="_blank" rel="noopener">https://techcrunch.com/2019/02/21/redis-labs-changes-its-open-source-license-again/</a>)</p>
</li>
<li>
<p>NoSQL Databases: An Overview (<a href="https://www.thoughtworks.com/insights/blog/nosql-databases-overview"target="_blank" rel="noopener">https://www.thoughtworks.com/insights/blog/nosql-databases-overview</a>)</p>
</li>
<li>
<p>A SHORT HISTORY OF DATABASES: FROM RDBMS TO NOSQL &amp; BEYOND (<a href="https://www.3pillarglobal.com/insights/short-history-databases-rdbms-nosql-beyond"target="_blank" rel="noopener">https://www.3pillarglobal.com/insights/short-history-databases-rdbms-nosql-beyond</a>)</p>
</li>
<li>
<p>Redis has a license to kill: Open-source database maker takes some code proprietary (<a href="https://www.theregister.co.uk/2018/08/23/redis_database_license_change/"target="_blank" rel="noopener">https://www.theregister.co.uk/2018/08/23/redis_database_license_change/</a>)</p>
</li>
<li>
<p>MySQL Performance: MyISAM vs InnoDB (<a href="https://www.liquidweb.com/kb/mysql-performance-myisam-vs-innodb/"target="_blank" rel="noopener">https://www.liquidweb.com/kb/mysql-performance-myisam-vs-innodb/</a>)</p>
</li>
<li>
<p>Why Amazon DynamoDB isn’t for everyone (<a href="https://read.acloud.guru/why-amazon-dynamodb-isnt-for-everyone-and-how-to-decide-when-it-s-for-you-aefc52ea9476"target="_blank" rel="noopener">https://read.acloud.guru/why-amazon-dynamodb-isnt-for-everyone-and-how-to-decide-when-it-s-for-you-aefc52ea9476</a>)</p>
</li>
<li>
<p>SQLite vs MySQL vs PostgreSQL: A Comparison Of Relational Database Management Systems (<a href="https://www.digitalocean.com/community/tutorials/sqlite-vs-mysql-vs-postgresql-a-comparison-of-relational-database-management-systems"target="_blank" rel="noopener">https://www.digitalocean.com/community/tutorials/sqlite-vs-mysql-vs-postgresql-a-comparison-of-relational-database-management-systems</a>)</p>
</li>
<li>
<p>When Should I Use Amazon Aurora and When Should I use RDS MySQL? (<a href="https://www.percona.com/blog/2018/07/17/when-should-i-use-amazon-aurora-and-when-should-i-use-rds-mysql/"target="_blank" rel="noopener">https://www.percona.com/blog/2018/07/17/when-should-i-use-amazon-aurora-and-when-should-i-use-rds-mysql/</a>)</p>
</li>
<li>
<p>Why Uber Engineering Switched from Postgres to MySQL (<a href="https://eng.uber.com/mysql-migration/"target="_blank" rel="noopener">https://eng.uber.com/mysql-migration/</a>)</p>
</li>
<li>
<p>MySQL Might Be Right for Uber, but Not for You (<a href="https://dzone.com/articles/on-ubers-choice-of-databases"target="_blank" rel="noopener">https://dzone.com/articles/on-ubers-choice-of-databases</a>)</p>
</li>
<li>
<p>Showdown: MySQL 8 vs PostgreSQL 10 (<a href="http://blog.dumper.io/showdown-mysql-8-vs-postgresql-10/"target="_blank" rel="noopener">http://blog.dumper.io/showdown-mysql-8-vs-postgresql-10/</a>)</p>
</li>
<li>
<p>Apache Cassandra Architecture Fundamentals (<a href="https://blog.yugabyte.com/apache-cassandra-architecture-how-it-works-lightweight-transactions/"target="_blank" rel="noopener">https://blog.yugabyte.com/apache-cassandra-architecture-how-it-works-lightweight-transactions/</a>)</p>
</li>
<li>
<p>Highly available Redis Architecture (<a href="https://medium.com/kokster/highly-available-redis-architecture-613c89f887b4"target="_blank" rel="noopener">https://medium.com/kokster/highly-available-redis-architecture-613c89f887b4</a>)</p>
</li>
<li>
<p>Paper Review: Dynamo: Amazon’s Highly Available Key-value Store (<a href="https://sookocheff.com/post/databases/dynamo/"target="_blank" rel="noopener">https://sookocheff.com/post/databases/dynamo/</a>)</p>
</li>
<li>
<p>Using the HTC G1, 10 years later: 2008&rsquo;s smartphone is effectively a dumbphone in 2018 (<a href="https://www.androidpolice.com/2018/12/24/using-htc-g1-10-years-later-2008s-smartphone-effectively-dumbphone-2018/"target="_blank" rel="noopener">https://www.androidpolice.com/2018/12/24/using-htc-g1-10-years-later-2008s-smartphone-effectively-dumbphone-2018/</a>)</p>
</li>
</ul>
<p>=== Script</p>
<p>Olá, Fabio Akita</p>
<p>Este é o terceiro episódio do tema de devops pra encerrarmos o assunto e ao mesmo tempo vamos finalmente encerrar a série &ldquo;Começando aos 40&rdquo; neste décimo quarto episódio.</p>
<p>No episódio da semana passada eu disse que iria explicar sobre bancos de dados. Eu tentei escrever esse script diversas vezes de diversas formas nos últimos dias, mas é um assunto muito mais complexo do que caberia num único episódio. Eu não vou entrar em detalhes de como eles são implementados, os algoritmos e arquitetura. Eu queria falar sobre clustered indexes, bloom filters, write ahead logs, mvcc, consistent hashing e muito mais. Em vez disso quero só compartilhar algumas opiniões que talvez nem todos concordem, mas que deve ser o mais útil pra maioria das pessoas.</p>
<p>Então quero aproveitar pra falar muito rapidamente de uma coisa que não mencionei no episódio anterior quando falei sobre máquinas virtuais e containers, o tal de Function as a Service ou FaaS que muitos ficam hypando hoje em dia. Finalmente vou fechar com um pensamento de como tudo isso que eu expliquei nos últimos episódios afeta o mundo de código livre. Hoje é dia do Endgame, fiquem comigo!</p>
<p>(&hellip;)</p>
<p>Como primeiro assunto, vamos falar rapidamente sobre bancos de dados e arquitetura. Se você estiver mais interessado em ir direto pra parte de Function as a Service vá pra esse tempo indicado aqui embaixo. E se quiser ir direto pra conclusão, vá pra este outro tempo aqui embaixo.</p>
<p>Se você é iniciante eu não recomendo tentar instalar e configurar manualmente nenhum banco de dados sozinho. Nem os bancos relacionais como MySQL ou Postgres e nem os NoSQL como Redis, MongoDB ou Cassandra. Não adianta só seguir um tutorial e fazer rodar. Garantir que você não vai perder dados importantes é muito mais do que meramente fazer um servidor rodar. Seguir um tutorial é absolutamente trivial.</p>
<p>Todos os bancos tem dezenas de configurações diferentes que devem ser planejadas exclusivamente pro uso da aplicação. Não existe uma única configuração que serve universalmente pra tudo. E não, NoSQLs não são mais fáceis de configurar ou dar manutenção do que os bancos relacionais. Se você tem essa noção de algum lugar, tire isso da sua cabeça. Eu diria que uma infraestrutura NoSQL de verdade dá tanto trabalho ou mais do que bancos relacionais. Especialmente se realmente formos eliminar pontos únicos de falha e tivermos requerimento de estratégias de recuperação de desastres com zero downtime, ou seja, o banco sempre estar disponível mesmo que um dos servidores do cluster sofra algum problema.</p>
<p>Se você for programador aprenda SQL e o modelo relacional corretamente. Você precisa saber como fazer queries decentes. A pior parte de uma aplicação é quando o desenvolvedor acredita que a biblioteca de mapeamento objeto-relacional que ele está usando, seja Hibernate no Java, SQLAlchemy no Python, ActiveRecord no Ruby, Eloquent ORM no PHP, nenhum deles vai fazer queries eficientes pra você. E queries ineficientes é um dos principais ofensores em desperdício de memória e performance. Não adianta trocar de banco se seu código é uma porcaria, nenhum banco vai fazer mágica.</p>
<p>Muita gente acha que usar um MongoDB é bala de prata pra combinar facilidade de uso e performance, especialmente pra iniciantes. Se você não assistiu meu episódio sobre o livro The Mythical Man Month, aqui vai um resumo: não existe bala de prata!</p>
<p>Pra começar, se você subir uma instância de MongoDB e deixar ele sozinho, você já começou errado. MongoDB não tem garantias de 100% de durabilidade. O melhor caso de uso do Mongo é que o dataset caiba inteiro em RAM. Todas as operações são feitas em RAM, é o que garante sua performance. Mas isso significa que se a máquina der pane antes da RAM ter chance de dar flush pro disco, você vai perder dados. Você pode configurar pra que ele garanta mais durabilidade. Por exemplo, hoje quando você manda gravar alguma coisa no Mongo ele devolve ok antes de ter realmente gravado. O ok do Mongo é mais tipo &ldquo;bele, recebi a ordem pra gravar, quando der eu gravo&rdquo;. Diferente de um banco relacional que só dá ok se garantidamente o dado foi gravado. Você pode configurar o Mongo pra fazer o mesmo, devolver ok só se garantidamente uma outra instância de replica master recebeu o dado. Mas aí você perde a performance que era justamente o que te fez escolher Mongo em primeiro lugar.</p>
<p>Aliás, uma instalação mínima de Mongo precisa de pelo menos duas instâncias em replica set. Eu disse que o sweet spot é o dataset caber em RAM, mas você não tem RAM infinita, se você pretende armazenar ordens de grandeza mais dados do que cabe em RAM, talvez precise fazer shards. Mas configuração de shard precisa ser planejada do começo. Mudar de uma configuração sem shard pra ter shard não é só ligar ou desligar uma chave, você vai ter que refazer toda a infraestrutura do zero e mover todos os dados de lugar se escolher errado. Some replicação e sharding pra configurar e você criou um emaranhado de gato pra resolver. Portanto não, um MongoDB não é mais simples do que qualquer outro banco se você realmente vai usar da forma certa. Instalar na sua máquina só pra brincar, qualquer banco é simples, não só o Mongo.</p>
<p>Um NoSQL wide column store como Cassandra, apesar de ter similaridades com bancos relacionais só na superfície, especialmente nos schemas e no CQL que é similar a SQL, não tem absolutamente nada a ver. Eles são como key-value stores multi dimensionais com column families e foram feitos pra serem bancos altamente distribuídos, com múltiplos nós num cluster, de preferência em múltiplas regiões diferentes. Cassandra não foi feito pra usar numa única instância, como banco de um blog. O sweet spot dele é funcionar em cluster, com múltiplos nós, e com muita escrita.</p>
<p>Aliás, a maioria dos NoSQL é alguma derivação de uma estrutura de chave e valor, os key-value stores, como um Hash ou um Dicionário que qualquer linguagem de programação tem. O próprio Mongo pra mim é um key-value onde o value é um documento JSON, por isso chamamos de Document store. O sweet spot deles é localizar um valor tendo a chave em mãos. Diferente de um banco relacional, a maioria não é nem um banco transacional e nem um banco analítico, ou seja, que você pode sair fazendo queries como em SQL com qualquer critério e a qualquer momento. Pesquisas em bancos NoSQL precisam ser planejadas, indexadas com antecedência. NoSQLs tendem a ser bancos que você quer que funcionem em cluster, com shards, ou seja, nenhum servidor tem todos os dados que você precisa ao mesmo tempo, e se sua pesquisa for mal planejada, ela vai ter que ir vasculhando em todos os nós da rede, e isso custa processamento e custa tempo, muito tempo.</p>
<p>E um aviso muito importante que eu já dei em outros episódios: você não é o Facebook, nem o Google, nem o Netflix, nem o Uber. As experiências de engenharia dentro do Uber só tem valor pra outro produto com o mesmo nível de escala do Uber. Aceite a seguinte realidade: os 99,999% de nós jamais vai chegar a uma fração da escala do Uber. Portanto as decisões mais críticas de infraestrutura que eles relatam e que dão excelentes blogs posts e palestras são quase como ficção científica pra gente: é legal de ver, mas não nos impacta de nenhum jeito.</p>
<p>Um exemplo disso foi um blog post incendiário de um engenheiro do Uber de 2016 intitulado: &ldquo;Por que a engenharia do Uber migrou de Postgres pra MySQL&rdquo; acusando o Posgres de ter uma &ldquo;arquitetura ineficiente pra escritas&rdquo;. Se você ler só isso vai sair que nem um idiota dizendo &ldquo;puuts, temos que migrar nossos Postgres pra MySQL porque ele tem defeitos de escrita&rdquo;. Tem idéia de qual imbecil isso soa? Pra começo de conversa, o teor do artigo se refere a limitações do Postgres em casos extremos de grande volume de updates ou atualizações. E novamente, na escala do Uber. Tanto MySQL quanto Postgres são excelentes escolhas pra todos os casos que você precisa de um banco relacional, e que por acaso é a grande maioria dos casos. Programador é muito crédulo, fica acreditando em conto do vigário toda hora, é impressionante. Parem com isso!</p>
<p>Na prática a maioria de nós vai sempre acabar usando uma combinação de um banco relacional com um cache, usando algo como um Redis. Nessa configuração o Redis pode até ser um pouco mais relaxado porque o certo é fazer com que os dados sejam consultados no Redis e o que não estiver lá você carrega do banco relacional e grava no Redis à medida que for precisando. Portanto o Redis pode ser reconstruído do zero mesmo se der pane e precisar derrubar tudo. E você usa o Redis pra manter coisas pesadas de calcular e gravar num banco relacional como agregações, coisas com contadores, médias e outras métricas que agregam múltiplas linhas do banco.</p>
<p>Num banco relacional, o que mais custa performance é escrever, mais do que ler. A escrita precisa ter o tal ACID garantido: atomicidade, consistência, isolamento e durabilidade. Dados escritos precisam garantidamente estar no disco de tal forma que uma pane inesperada não faça você perder dados. Múltiplas conexões escrevendo ao mesmo tempo não podem um pisar no pé do outro, você precisa de um esquema de concorrência como multiversion concurrency control ou MVCC que a maioria dos bancos usa hoje em dia. E você precisa de transaction logs pra conseguir dar rollback ou voltar ao estado original dos dados se uma transação de múltiplas operações dá pau na metade.</p>
<p>Numa aplicação com grande pressão de escrita, digamos um ecommerce em dia de Black Friday, fechando várias compras por segundo. Você deve programar sua aplicação pra usar filas. Toda nova compra entra na fila e vai sendo escrito no banco à medida que der. Você nunca pode só aumentar a quantidade de servidores de aplicação web e deixar conectar todo mundo no mesmo banco ao mesmo tempo, isso só vai aumentar a contenção e no final ninguém consegue escreve. Então é melhor todo mundo entrar numa fila e só a fila gerenciar o banco. As leituras usam caches de Redis e isso tira a carga de pesquisa no banco. O grande gargalo numa aplicação web é o banco de dados, porque mesmo com replicação e sharding, você sempre vai ter contenção por causa das garantias ACID.</p>
<p>Além disso, num banco relacional você aprende na faculdade a idéia de normalização, onde separa os fatos em tabelas e faz referências entre as tabelas, de forma a diminuir ao máximo a redundância dos dados entre tabelas. Do ponto de vista de modelagem, assim é mais bonito. Mas uma verdade é que do ponto de vista de performance, quanto mais você normalizar, pior pode ser a performance. Tanto bancos relacionais quanto NoSQL privilegiam tabelas denormalizadas. Você faz trade-off de redundância e mais uso de disco pra ter mais performance. Uma coisa cara no processamento de pesquisas é justamente fazer joins entre tabelas, por isso bancos como Postgres oferecem recursos como views materializadas. Views é como se fosse uma tabela lógica, que não existe, porque é o resultado dinâmico de uma query, mas uma view materializada é como se fosse uma tabela denormalizada de verdade.</p>
<p>Ou seja, numa aplicação de verdade, você talvez se veja obrigado a denormalizar algumas tabelas. Um jeito errado de usar um MongoDB ou Cassandra por exemplo, é granular as coisas demais e tentar fazer o equivalente a joins. NoSQL não foi feito pra ter joins e relacionamentos. E você vai descobrir que muitos problemas de performance que você tem com bancos relacionais é normalização demais. Pense em sharding e se você deixar uma tabela num servidor, outra tabela com dependências em outro servidor, como você vai fazer joins rápida entre tabelas em servidores diferentes? Como tudo em computação, não existe uma forma de quanto é normalizar demais. É uma arte, muita tentativa e erro em diferentes casos de uso.</p>
<p>Um exemplo bem idiota, a famosa tabela de estados. A grande maioria dos programadores faz uma tabela separada chamada estados com uma coluna de código pra colocar por exemplo SP e outra coluna de nome pra colocar São Paulo. Daí numa outra tabela de cadastro você tem um campo estado_id que é uma referência pra linha da tabela de estados. Agora toda vez que você puxar o cadastro precisa fazer um join com a tabela de estados. Eu acho isso desnecessário. Eu colocaria SP e São Paulo direto na linha da tabela de cadastros e um select simples, sem join, vai devolver tudo que eu preciso. Mas aí toda linha vai duplicar São Paulo toda vez. Foda-se. A probabilidade do estado de São Paulo mudar de nome é praticamente zero. Se um dia mudar sim, vai custar um pouco caro dar update em todas as linhas de todas as tabelas que ficou com isso duplicado, mas isso é uma possibilidade remota. Pra que eu vou normalizar dados que nunca mudam?</p>
<p>Mas isso significa que não pode haver o caso de alguém mandar gravar São Paulo com til e de outro lugar alguém mandar gravar um Sao Paulo sem acento. A letra C de consistência do ACID vem migrando do banco pra aplicação faz algum tempo e se você começar a denormalizar, vai ter que garantir essa parte da consistência na aplicação. Vamos assumir que seu código não é porco, que você colocou enums no seu código e validações antes de mandar gravar no banco. Lógico, novamente é um trade-off, vou trocar performance por mais trabalho. Como eu disse, computação é uma arte, não existe almoço de graça, se pra você performance é mais importante, vai ter mais trabalho. Se pra você conveniência é mais importante, você paga tendo menos performance. Aliás, pra ter mais segurança, mais disponibilidade, mais durabilidade, mais integridade, tudo isso você paga tendo menos performance. Se quiser aumentar a performance, alguns desses fatores devem cair, por isso fica esperto!</p>
<p>E no final o que você vai ter com mais frequência é uma solução híbrida. Independente da sua linguagem favorita ou framework web, o mais comum é você usar um banco relacional. Eu pessoalmente gosto de Postgres mas não há nenhum motivo pra não usar MySQL com InnoDb ou mesmo seus forks como as versões da Percona ou o MariaDB. Você vai acabar movendo a consistência pra aplicação, com boas validações e bons testes no seu código. Também vai acabar denormalizando algumas tabelas pra evitar joins desnecessários, sempre criar índices corretamente, mas também evitar criar índices demais porque quanto mais índices pior a performance de escrita no banco.</p>
<p>Você também quer evitar gargalos de leitura e gargalos de escrita. Pra otimizar a leitura, você quer colocar um cache na frente do banco relacional. Você vai programar pra primeiro procurar o que quer num Redis. Se não achar lá, ou seja, tiver um cache miss, daí vai no banco relacional puxar os dados e gravar no Redis pra estar lá da próxima vez; com algum período de tempo pra expirar esse dado. E opcionalmente, se não quer o risco de parar o banco caso venham escritas demais, você vai programar sua aplicação pra colocar o pedido de escrita numa fila, seja no próprio sistema de publish/subscribe do Redis ou numa fila de verdade como RabbitMQ. Significa que o tempo geral de escrita deve ser mais lento do que mandar direto pro banco, mas se por acaso vier um pico inesperado que o banco não está preparado pra receber, em vez dele dar timeout e perder as operações, é sempre melhor ficar mais lento mas que garantidamente uma hora vai executar.</p>
<p>E não, programar com um framework assíncrono, seja Node.js ou qualquer outro que hoje em dia suporte async/await também não é uma bala de prata. Ajuda um pouco, mas não resolve o problema de picos e gargalo de concorrência no banco. No dia da Black Friday o que resolve o problema são filas, estruturas que você consegue controlar. async/await são boas pra ajudar, mas são só mais uma ferramenta e nunca a única solução que vai resolver tudo.</p>
<p>E se você precisar fazer uma coisa chamada Full text Search, que é pesquisas como você faz numa Amazon, onde você encontra produtos similares e tem resultados por relevância, você tem suporte a isso tanto no MySQL quanto no Postgres, mas na idéia de tirar o máximo de processamento do banco, o certo é manter um outro serviço só pra isso, no caso um Elasticsearch que é um dos melhores pra full text search. Toda vez que você escrever no banco relacional, também manda escrever no Elastic. Novamente vamos gastar mais recursos pra ganhar performance dos dois lados. Trade-off.</p>
<p>Se você usar Heroku, significa sua aplicação web rodar em um ou mais dynos web, e você ter um ou mais workers assíncronos que rodam em dynos de workers. Daí um Heroku Postgres, configurado com um follower pra onde você vai redirecionar as queries mais pesadas. Adicione também o Heroku Redis ou outro SaaS como Redis Cloud pra cache. Eu adicionaria um Cloudinary se sua aplicação precisar armazenar e processar uploads, um SendGrid se precisar mandar e-mails, um Rollbar pra guardar logs de erro, um New Relic RPM pra monitorar a performance da aplicação e um Bonsai Elasticsearch pra funcionalidades de pesquisa avançada. Espere gastar algo em torno de 200 a 300 dólares ou uns 1000 reais por mês. Parece muito? Não é, porque se você pretende instalar e configurar e manter tudo sozinho, você precisa de um sysadmin que vai te custar 5 a 10 vezes isso por mês. E isso só compensa se você for manter pelo menos 10 aplicações iguais a essa.</p>
<p>Se você realmente for manter parte disso sozinho, você que é sysadmin vai acabar usando um IaaS como a AWS ou Google Cloud. Nesse caso vai configurar as coisas com Kubernetes, Helm, Terraform e mesmo assim o ideal vai ser manter o banco de dados gerenciado em produtos como o AWS RDS ou AWS Aurora ou Google Cloud SQL. O banco de dados é a pior parte pra se manter, acredite. O trabalho simplesmente não compensa a menos que você seja muito grande. Não estou nem dizendo tamanho do Uber, mas você precisa estar lidando com terabytes diários de dados críticos pra compensar uma solução sob medida.</p>
<p>É tudo uma questão de gerenciamento de risco, um trade-off entre pagar mais pra ter mais segurança. Quanto custa pro seu negócio ficar fora do ar por 2 dias numa Black Friday? Quanto custa pro seu negócio perder os dados financeiros de meio dia de negociações? Na verdade, quanto custa perder 1 dia dos seus dados? Se você disser que não tem nenhum problema, eu questionaria a importância da existência do seu produto.</p>
<p>Uma última dica de infraestrutura: independente se você colocar sua aplicação numa máquina virtual de um VPS ou no AWS EC2 ou em containers via Kubernetes, ou mesmo numa plataforma como serviço como o Heroku ou Openshift, em qualquer dos casos contrate a Cloudflare. Ele é um serviço que vai substituir seu DNS, toda vez que alguém digitar seu domínio num navegador, o tráfego vai todo pra Cloudflare primeiro, e ele vai tanto servir como CDN que é um cache pra coisas como as imagens e outros assets do seu site como vai servir de detecção e prevenção de ataques. Se algum hacker ou algum moleque com um script tentar invadir seu site, o Cloudflare vai ser sua primeira linha de defesa e por experiência vou dizer que eles são muito bons. Altamente recomendado. Custa caro, mas de novo, quanto custa pra você se alguém entrar no seu site e roubar os dados dos seus clientes? Ou causar um Denial of Service no seu ecommerce no pico da Black Friday?</p>
<p>Como segunda seção deste vídeo, deixa eu explicar Function as a Service ou FaaS, que são produtos recentes como o AWS Lambda ou Microsoft Azure Functions ou Google Cloud Functions. Eles são basicamente containers. Se você já usou coisas containers de Docker ou dynos de workers do Heroku, a idéia é similar. A implementação varia entre os diferentes fornecedores então cuidado porque não é um padrão! Você configura um evento ou um trigger, que pode ser várias coisas, uma chamada HTTP, um pub/sub de algum serviço de fila como o AWS SQS, ou o Firebase no caso do Google Cloud. Quando esse evento vier, ele vai subir um container pequeno que vai executar essa sua tal lambda ou função que está englobada num frameworkzinho específico pra plataforma. Dizer lambda ou functions é mais marketing do que qualquer outra coisa, se você ainda não tinha notado, pra aproveitar esse hype em cima de linguagens funcionais, mas não tem nenhuma relação.</p>
<p>Os containers já vem pré-configurados com imagens pra diversos frameworks como Node.js, ou Ruby, ou Go, ou Python, ou Java, ou C#. A idéia não é rodar um framework web monolítico como Ruby on Rails, ou ASP.NET MVC, ou Play Framework. Repetindo: sim, como é um container comum, que roda uma linguagem conhecida, que inclusive permite você instalar dependências externas, então tecnicamente você pode rodar o quiser nesses containers. Mas só porque você pode, não quer dizer que você deve. Sempre existe a ferramenta certa pro problema certo e na minha opinião FaaS não foram feitos pra hospedar sites grandes inteiros.</p>
<p>Máquinas virtuais de um IaaS como AWS EC2 ou mesmo num VPS tendem a ter um SLA alto. Eles não garantem que a máquina vai ficar de pé pra sempre, mas a intenção é ficar de pé por um longo período de tempo. Semanas ou meses. Num PaaS como Heroku, os containers que ele chama de dynos também tendem a ficar de pé por longos períodos, dias ou semanas. Mas não é uma garantia, por isso ele força você a não depender de gravar coisas no disco local, porque os dynos são containers voláteis. Você deve programar de tal forma que se um dyno restartar, a aplicação reinicie sem problemas e tudo que você precisa está em banco de dados ou caches externos.</p>
<p>Um container de função tende a ser menor e a ficar de pé por períodos ainda menores, horas, no máximo dias. A intenção é que você não se preocupe de manualmente ter que subir esses containers. Eles vão sendo iniciados a partir desses triggers ou eventos. A função deveria receber os dados do evento, processar o que precisar o mais rápido possível, e assumir que ele pode desligar depois disso. Na prática o AWS ou Google vai manter o container de pé por mais algum tempo caso outros eventos estejam esperando na fila. Mas se ficar sem fazer nada por muito tempo ele deveria desligar pra não desperdiçar recursos.</p>
<p>Como é possível receber um trigger de tipo HTTP ou seja, você poderia apontar seu navegador pra uma URL que seja gatilho pra ativar uma função, e de dentro da função você poderia conectar num banco de dados e devolver um HTML, você pode ficar tentado a colocar um site, ou pior, uma aplicação web inteira. E vai funcionar. Existem alguns frameworks web mais enxutos feitos pra tentar tirar vantagem disso.</p>
<p>Se você não tem problema em ter alta latência e tempo de resposta demorado de tempos em tempos, pode funcionar sim. Mas você precisa garantir que sua aplicação seja pequena. Como os containers de funções podem desligar a qualquer momento, quanto maior for sua aplicação mais vai demorar pra ele levantar um novo container no próximo evento. Eu pessoalmente acho que usar AWS Lambda e outros FaaS pra servir aplicações web é o uso errado da tecnologia.</p>
<p>Lembram como eu falei agora pouco que uma das formas de evitar gargalos no banco de dados é jogar o pedido de escrita numa fila e depois um worker que ouve essa fila ir escrevendo no banco? Esse seria um jeito. Você enfileira os pedidos no AWS SQS que é uma fila, conecta ele como event source no AWS Lambda, faz uma função que conecta no AWS Aurora e aí grava no banco. Ou outras coisas pesadas pra sua aplicação como processar imagens ou vídeos. Você manda uploads da sua aplicação pro AWS S3, daí manda enfileirar a ordem no AWS SQS de novo, que vira event source pra um Lambda que vai puxar o arquivo do S3, processar e jogar de volta no S3 ou manda arquivar no AWS Redshift.</p>
<p>Eu diria, de maneira simplória, que o AWS Lambda está pra AWS como um dyno de worker está pro Heroku. Ele não é nenhuma grande revolução como muito palestrante ou blog post faz parecer. Ele é mais um sub-produto do uso de containers pra tentar desperdiçar menos recursos. Em vez de você subir um EC2 inteiro e deixar ele esperando sem fazer nada até vir alguma coisa pra rodar, melhor criar um container que vai ficar simplesmente desligado se não tiver nenhuma atividade. Ele é melhor pra aplicações que não tem tanta atividade assim o tempo todo, por exemplo, quando de noite ou de fins de semana não vem nenhum novo evento ou vem muito pouco.</p>
<p>E nos casos onde sua aplicação tem picos difíceis de prever, com esse sistema ele pode subir múltiplos containers se a fila estiver com muita pressão. Então Function as a Service é só isso: um container com um código simples dentro de uma função, preferencialmente, que vai responder a eventos pré-configurados, seja de uma fila, seja um gatilho via HTTP, com o mínimo possível de configuração.</p>
<p>Como terceiro e último tema de hoje, quero compartilhar um pensamento com vocês. Eu não sou um idealista, eu não sou um ativista: eu gosto de pensar que sou mais uma testemunha da história. Se possível eu prefiro tentar explicar as coisas da forma como eu vejo, mas não julgo ninguém por estar de um lado ou de outro porque só a história vai realmente dizer quem estava certo. Eu prefiro estar atento e me mantendo anti-frágil às mudanças. Quem leu Nassim Taleb, sabe o que isso significa.</p>
<p>Faz uns 30 anos que estamos vivendo num mundo de tecnologia dividido entre o mundo de código livre e o de código proprietário. Todos sabem que até o fim dos anos 90 o mundo de código proprietário sempre foi dominante. Mas principalmente a partir do novo século que o mundo de código livre evoluiu muito rápido. Hoje em dia todo desenvolvedor que se preza usa ferramentas derivadas do mundo de código aberto. Tudo que eu vim falando nessa série toda, incluindo distribuições Linux, Docker, Kubernetes, MySQL, Redis, tudo isso é mundo de código livre.</p>
<p>Em 1995 Bill Gates soltou o famoso memorando interno na Microsoft explicando a ameaça do navegador à hegemonia do sistema operacional Windows e outros produtos como o Office. Em breve o navegador substituiria o sistema operacional. E ele estava certo.</p>
<p>Considere o seguinte: antigamente precisaríamos comprar um servidor de e-mails como um Microsoft Exchange. Finalmente surgiram opções de código aberto como Zimbra, Open Xchange e vários outros. A mesma coisa foi acontecendo com todo software proprietário. E por um tempo pareceu que o modelo estava funcionando e o mundo open source foi ganhando força até que as versões abertas passaram a superar as fechadas em muitos lugares, em particular em tecnologias de web, redes, servidores e infraestrutura.</p>
<p>Então surgiu o Gmail em 2006 e na sequência a Amazon e seu AWS, que inaugurou o conceito de empacotar as coisas como serviço, e expor esses serviços como APIs, em uma escala que não se imaginava. E muitas empresas seguiram o mesmo modelo, oferecendo software como serviço. Antes você pagava licenças caras pra comprar ou assinar pelo uso de software proprietário. O que mudou agora é que com os canais de distribuição da internet podemos fazer a mesma coisa que antes, que era comprar software encaixotado de prateleira ou via consultoria ou vendedores. Então o que realmente mudou foi o preço ser muito menor porque o software consegue chegar em mais pessoas e empresas.</p>
<p>Mas note: voltamos ao mesmo modelo dos anos 90. Você paga uma assinatura mas não é proprietário do software. Pior: você não tem acesso ao código do software, porque ele funciona fechado atrás do que chamamos de um walled garden ou jardim emparedado. É um jardim bonito, mas lembre-se que ele não é aberto. Apesar da Amazon, Google, Facebook, Netflix, Uber e todos os outros de fato colaborarem ainda com software livre porque seria burrice não fazer isso, já que todos usam Linux e outras ferramentas.</p>
<p>Mas o software que torna a Amazon a Amazon ou o Google o Google jamais vai ser aberto. O AWS é o Windows da Amazon. O Google G Suite é o Office do Google. Custa muito caro criar uma versão aberta que seja igualmente competitiva, especialmente porque estamos falando de larga escala. Quanto mais eles crescerem, mais difícil vai ser pra qualquer outra empresa conseguir competir com eles. Então é um mercado com uma barreira de entrada enorme. E você basicamente trocou o Microsoft Office proprietário pelo Google Docs que também é proprietário.</p>
<p>Na prática, a maior parte do software que realmente dependemos no nosso dia a dia é proprietário, mas achamos que está tudo bem porque roda num navegador e não no Windows, mas tudo que tem acesso livre na verdade é pago com os dados da sua privacidade - e eu nem sou necessariamente contra, mas se você de alguma forma achava que é idealista de software livre usando tecnologias do Google ou Facebook, hoje já está num mundo fechado na sua maior parte, e você é equivalente a ser evangelista da Microsoft nos anos 90. De novo, nenhum problema nisso, só esclarecendo que não é o que você achava.</p>
<p>Considere que muito do que hoje é mantido como software livre não são nada mais do que bibliotecas que consomem APIs. Eles sozinhos não servem pra nada, são apenas cascas em cima de clientes de HTTP. Toda biblioteca da Amazon, do Google, do Facebook conectam com os serviços deles e só servem pra isso. Muitos desenvolvedores de software livre como o Salvatore que criou o Redis tem reclamado disso faz meses. Eles chegaram a mudar a licença pra uma mais controversa que impede que empresas como Amazon ou Google possam usar produtos como o RedisSearch, RedisGraph, RedisJSON e outros produtos.</p>
<p>Aos poucos você vê uma Amazon agindo como a Microsoft dos anos 90, adotando software de fora, e criando modificações que atraiam as pessoas pra sua versão proprietária e depois fechar as pessoas dentro de seu walled garden. Veja o AWS Aurora que é um MySQL modificado e cuja modificação não é open source. Daí eles lançam um AWS Lambda e uma gama de outros produtos que basicamente vão prender você dentro da AWS. Uma vez que você adotar esses produtos e seu negócio realmente der certo, você não vai mais sair de lá. Lembra o que os desenvolvedores reclamavam de Windows e Office? A AWS é o novo Windows e Office caso ainda não tenham percebido.</p>
<p>Eu comecei a pensar em outra coisa por causa de retrogaming e meu hobby gostando de tecnologias antigas. Se eu pegar um computador dos anos 80, um Commodore 64, eu consigo rodar todos os softwares daquela época. Se eu pegar um Gameboy, ainda consigo rodar todos os cartuchos. E como foram feitos dumps dos cartuchos, CDs e DVDs, então todos os jogos foram preservados em algum site. Essa parte do passado está preservada pra sempre.</p>
<p>Mas se eu pegar um celular Android de primeira geração, o HTC G1, muitos dos serviços nele não funcionam. Porque eles são só cascas pra software as a service que já não existe mais. Vários hardwares de 10 anos atrás vão começar a falhar sem serviços pra conectar. Consoles de videogame é outro exemplo. Se a Playstation Network decidir que nenhum PS3 pode mais logar na rede deles, você basicamente perdeu o uso desse hardware. Todo jogo online daqui a 20 anos, sem os servidores proprietários, não vão servir pra mais nada. Não há como preservar mais esse software. Daqui 40 anos uma geração não vai ter como saber o que se usava nos anos 2010 a não ser por fotos de tela. Eu posso rodar o Office de 1995 hoje se quiser, mas não poderemos mais rodar o Google Docs de 2019 daqui a alguns anos.</p>
<p>E a direção dos software as a service indicam uma mudança de paradigma. Um mundo ideal de um Uber é onde a maioria das pessoas não precisasse mais comprar carros. Uber é carro como serviço. Pra um AirBnb o mundo ideal é onde a maioria das pessoas não precisasse mais comprar casas. AirBnb é casa como serviço. iFood é comida como serviço. Netflix é entretenimento como serviço. Spotify é música como serviço, você não tem mais propriedade sobre sua mídia de música ou filmes. E assim por diante.</p>
<p>Como eu disse antes, em tecnologia tudo é um trade-off. Estamos trocando propriedade por conveniência. Eu não digo que isso é bom ou ruim, o veredito ainda não foi definido. Mas eu achei interessante colocar como pensamento final desta série. O mundo da tecnologia desta década é diferente de 10 anos atrás, que foi diferente de 20 anos atrás, então não espere que daqui a 10 anos vai ser igual agora, você pode esperar uma mudança geral de ecossistema e tecnologias mais ou menos a cada 10 anos.</p>
<p>E com esse pensamento, eu tenho o prazer de finalmente encerrar esta série! Eu realmente não pensei muito quando comecei. Imaginava que fosse encerrar em 3 ou 4 episódios e acabou se tornando uma mini-série de 14 episódios! Eu publiquei o primeiro episódio em 22 de Janeiro e não pulei nenhuma semana. E eu quis encerrar exatamente hoje porque coincidiu de ser o dia do Endgame, claro!</p>
<p>Depois desse projeto eu quero tirar uma ou duas semanas de folga do canal e ver o que quero gravar a seguir. Não tenho dúvidas que só toquei na ponta do iceberg ainda, mas espero que tenha atiçado a curiosidade de todo mundo que queira se tornar um desenvolvedor. Mandem dúvidas nos comentários abaixo e sugestões de novos assuntos. Se curtiram o vídeo mandem um joinha, e por favor compartilhem o video pra ajudar o canal. Não esqueçam de assinar abaixo e clicar no sininho pra saberem quando eu voltar. A gente se vê na próxima, até mais!!</p>
]]></content:encoded><category>bancos-de-dados</category><category>cloud</category><category>arquitetura-de-software</category><category>akitando</category></item><item><title>[Akitando] #34 - Você não sabe nada de Enterprise. Conhecendo a SAP!</title><link>https://akitaonrails.github.io/2018/12/26/akitando-34-voce-nao-sabe-nada-de-enterprise-conhecendo-a-sap/</link><guid isPermaLink="true">https://akitaonrails.github.io/2018/12/26/akitando-34-voce-nao-sabe-nada-de-enterprise-conhecendo-a-sap/</guid><pubDate>Wed, 26 Dec 2018 17:00:00 -0200</pubDate><description>&lt;p&gt;Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal &lt;a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener"&gt;Akitando&lt;/a&gt;. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.&lt;/p&gt;
&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/FXhcfJnlD2k"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;h3&gt;Descrição no YouTube&lt;span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"&gt;&lt;/span&gt;
&lt;a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Certamente ninguém ia ver video ontem, então fica meu presente de natal hoje mesmo :-) (26/12)&lt;/p&gt;</description><content:encoded><![CDATA[<p>Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal <a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener">Akitando</a>. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/FXhcfJnlD2k"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<h3>Descrição no YouTube<span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"></span>
    <a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Certamente ninguém ia ver video ontem, então fica meu presente de natal hoje mesmo :-) (26/12)</p>
<p>Finalmente, um dos episódios mais longos da minha carreira. Vamos entender um dos mercados menos compreendidos por toda a comunidade de programação.</p>
<p>Quase nenhum programador de agência, startup, tem idéia do que realmente é o tão temido mercado &ldquo;enterprise&rdquo;.</p>
<p>Então em vez de ficar falando mal sem saber, vou explicar o que diabos realmente é a SAP, tanto do ponto de vista de história quanto tecnologias.</p>
<h2>Script<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>Finalmente chegou o dia de eu contar uma das minhas histórias mais longas. Eu falei um pouco no vídeo de &ldquo;O que estudar&rdquo; sobre o que é ser um consultor. Então HOJE eu quero explorar mais sobre isso e também sobre um mercado que a grande maioria das pessoas não conhece: o famigerado mundo Enterprise, mais especificamente a vertente da SAP que eu conheci no começo dos anos 2000.</p>
<p>Eu já falei algumas vezes de quando eu estava trabalhando na ponto com PSN que fazia sites de esportes na América Latina. Todo mundo sabe que em 2001 a bolha das ponto com estourou com força, muitos acharam que a internet ia até acabar e eu estava numa dessas ponto com. De repente eu estava na rua, mas não por muito tempo.</p>
<p>Exatamente na semana que tudo explodiu eu recebi uma ligação, meu amigo e mentor Antélio Abe, que eu tinha conhecido anos antes lá por 1997 quando estava trabalhando pela minha agência num projetinho de intranet pra Camargo Corrêa. Em 2001 ele tinha saído da Camargo e estava numa pequena consultoria enterprise chamada Balance e eles estavam precisando de alguém que entendesse de tecnologias Web, coisa que ainda era meio alienígena no mundo enterprise. Ele me ofereceu pra aprender esse tal de SAP, e eu pensei, &ldquo;por que não?&rdquo;</p>
<p>(&hellip;)</p>
<p>Pra fazer esse episódio eu pedi ajuda de novo pro Antélio e somado com o que eu lembro, vou tentar organizar algumas informações que podem ser interessantes. Pra vocês entenderem o Antélio é um consultor “asterisco”, coisa rara quando um consultor é bom em múltiplos módulos funcionais de um ERP. Ele já participou de grandes projetos em empresas como Monsanto, Telemar/OI, Claro, Diageo e dezenas de outras.</p>
<p>Vamos dar uma recapitulada. Embora a SAP não seja nem de longe do tamanho do market cap de uma Microsoft, Google ou Apple, ela está instalada no coração das maiores empresas do mundo. Uma hipérbole que acho que não está muito longe da realidade é que eu digo que pra entender SAP, pegue a lista das 500 maiores empresas do mundo. Eu poderia apostar a maioria, ou quase todas, rodam soluções da SAP no seu backend.</p>
<p>SAP SE significa, do alemão pro inglês, Systems, Applications &amp; Product in Data Processing. Seu quartel general é em Walldorf na Alemanha com escritórios em 180 países e mais de 335 mil clientes. Sua gestão já foi muito centralizada, mas agora está espalhada pelo mundo inteiro, conseguindo suportar clientes 24x7 em qualquer fuso horário.</p>
<p>Em 2017 a receita da SAP foi de mais de 23 bilhões de dólares com lucro de mais de 4 bilhões. Ela tem quase 100 mil funcionários pelo mundo. Dentre as principais figuras da SAP ainda está um de seus co-fundadores, Hasso Platner. Não sei se ainda hoje é assim mas Hasso sempre foi o arqui-inimigo de outro titã do mundo enterprise, o famigerado Larry Ellison, da Oracle. Diz uma lenda urbana que eles disputaram uma corrida de iates e quando o Hasso passou na frente abaixou a calça e mostrou a bunda pro Larry. :-D</p>
<p>(aham) Uma curiosidade é que todo mundo se lembra do lendário laboratório da Xerox Parc, que foi a semente da revolução dos micro computadores dos anos 80, a fonte onde tanto a Apple quanto a Microsoft beberam para sair com coisas como o mouse e a interface gráfica. Mas além deles a Xerox ainda ia deixar outro legado pra indústria.</p>
<p>Segundo nossa amiga Wikipedia, a Xerox estava querendo sair da indústria de computadores até 1975 e pediu à IBM para migrar seus sistemas de negócios para tecnologia IBM. Então, cinco engenheiros da IBM que estavam trabalhando no software do sistema enterprise e lhes foi dito que esse software não seria mais necessário. Em vez de jogar tudo fora, eles decidiram sair da IBM Tech e começar outra empresa.</p>
<p>SAP é mais um dos casos em que o nome da empresa sobrepõe ao nome de seus produtos e sistemas. O nome da empresa inicialmente foi System Analysis and Program Development ou SAPD e seu primeiro cliente foi a Imperial Chemical Industries em Ostringen onde fizeram o software de folha de pagamento de contabilidade para mainframe. Mas em vez de guardar os dados em cartões perfurados como a IBM fazia na época, eles armazenavam locamente num banco de dados lógico. Por isso chamaram o sistema de &ldquo;real-time&rdquo; e é de onde vem a letra R do principal sistema integrado deles que, em 1973, foi lançado como R/98.</p>
<p>Em 1979 lançaram o SAP R/2 estendendo para outros módulos como gerenciamento de materiais e planejamento de produção. Daí, do mainframe eles pularam pra arquitetura cliente/servidor nos anos 90, o que chamávamos de arquitetura em 3 camadas, e com isso o nome foi de R/2 para R/3 em 1995.</p>
<p>No Brasil algumas das primeiras instalações de SAP foi na Dow Química e Dupont, ambas usando o R/2 de mainframe que serviriam de base para construção da localização Brasil. Em termos de SAP, localização envolve não só o óbvio que é traduzir todos os textos do sistema em português, mas também a Tropicalização que envolve regras de negócio específicas do nosso país como as regras tributárias, fiscais, CLT e tudo mais.</p>
<p>A primeira instalação do SAP R/3 no Brasil foi na Philips, que veio via a consultoria francesa Atos Origin que por si só tem uma história interessante, sendo originada do merge de duas empresas francesas e da holandesa Origin. Foi adquirida pela KPMG em 2002 e depois a Atos Origin anunciou a compra da Siemens IT que foi finalizada em 2011 sob o nome só de Atos.</p>
<p>Enfim, no Brasil a Atos Origin tinha sede onde hoje é o prédio da Locaweb em São Paulo. A própria Origin foi tipo uma spin off da área de TI da Philips e quem passou a representar a SAP no Brasil.</p>
<p>Uma coisa que vocês devem ter ouvido falar que aconteceu no fim dos anos 90 foi a corrida desenfreada pelo Bug do Milênio. Eu lembro disso porque eu mesmo trabalhei consertando esse bug em 1999, mas isso pode ser história pra outro dia. De qualquer forma as empresas baseadas em Mainframe analisaram a possibilidade de implementar SAP. Já que ia precisar lidar com o tal Bug do Milênio já aproveita pra substituir o legado por SAP.</p>
<p>Mas aí começou um empurra-empurra entre a então SAP AG da Alemanha e a Origin pra investir na localização Brasil. Ou seja extrair o que se tinha na Philips como um conjunto reusável de templates e customizações para ser o scaffold pra novas instalações. Vou mencionar localização depois mas entenda que isso não é uma coisa barata de fazer. Com esse impasse as multinacionais ameaçaram desistir da SAP, o que forçou a SAP a abrir a SAP Brasil e colocar a Origin de escanteio.</p>
<p>Durante a virada do século, eu vi uma das grandes transições da SAP, do mundo client/server para integrar tecnologias Web. Esse era um dos meus trabalhos. Na minha época estávamos no R/3 versão 4.6c.</p>
<p>Eu costumo explicar o R/3 da seguinte forma: comece imaginando uma máquina virtual, como um JVM. Essa camada parasita e se sobrepõe ao sistema operacional e também ao banco de dados relacional por baixo, como Oracle, DB/2 ou SQL Server. 99% dos consultores não sabem se é um UNIX ou um Windows ou o que rodando, eles só viam o R/3.</p>
<p>Essa máquina virtual é tão completa que ela abstrai o banco de dados relacional inteiro, incluindo a linguagem de acesso, permissões, schemas, dicionário de dados, e todas as ferramentas de administração. Essa é a camada que chamávamos de &ldquo;Basis&rdquo;, em linguajar do mercado o cara de Basis costuma ser tipo o sysadmin ou devops nos dias de hoje. E você acha que já viu banco de dados grande? Não, na minha época uma instalação padrão já pré-configurava mais de 40 mil tabelas (acho que hoje já é dobro disso).</p>
<p>Pense tabelas cujos nomes são acrônimos de palavras em alemão, então você tem tabelas como VBAK e VBAP pra cabeçalho de ordem de venda e ítens. Você em BUKRS pra código de empresa. Ou LIFNR pra fornecedor e assim por diante. Depois de algum tempo você decora muitos acrônimos, é bizarro que mais de 10 anos depois eu ainda lembro de muitos de cabeça.</p>
<p>Os acrônimos originalmente deveriam ser curtos: 4 caracteres para tabelas e 5 caracteres para nomes dos campos, tudo isto para economizar espaço em memória e banco de dados, afinal de contas em 1997 em pleno boom de SAP R/3 o processador mais top na época é um Pentium-II, sistemas unix mais fortes rodavam em 32 bits.</p>
<p>Na minha época havia o minisap que era só a camada Basis com as ferramentas de ABAP mas sem os módulos funcionais. Era mais pros sysadmin e devs poderem ter um sandbox pra brincar e treinar. Ainda existem versões mais novas disponíveis no SAP Trials, acessivel pra quem tem conta na SCN a SAP Community Network, qualquer pessoa pode-se increver, vou explicar isso mais abaixo. Mas pra entender, sem os módulos funcionais, ainda assim pesa nada menos que 15GB pra baixar e precisa de no mínimo 4GB de RAM e 100GB de HD pra instalação. ??? Existem algumas versões alternativas que podem ser instaladas via docker, mas ainda precisam do mesmo espaço → bizarro</p>
<p>Sobre essa máquina virtual roda uma linguagem proprietária chamada ABAP que é um acrônimo em alemão absurdo que significa &ldquo;general report creation processor&rdquo; ou processador de criação de relatório gerais. Ou seja, em muitos aspectos não é muito diferente de um PL/SQL da Oracle ou T-SQL do SQL Server, mas como linguagem ela é bem mais completa e hoje em dia tem recursos como orientação a objetos.</p>
<p>Como gerador de relatórios e também de formulários existe ainda o SAP GUI que é um cliente burro. Via ABAP eu posso construir telas e o que é mandado para o client são as instruções de como desenhar essa tela. Do lado cliente não roda nenhuma lógica de negócio, só lógica de apresentação. Você usa um derivado desse conceito todo dia: o navegador web, que é só um cliente burro que não roda nada e recebe instruções pra desenhar a tela. Pelo menos era assim até embutirem tanto Javascript … enfim, o SAP GUI é o navegador.</p>
<p>(telas)
Por exemplo, esta é a tela de Login, o SAP Logon, vc pode cadastrar login pra vários SAP.
Aqui você ver o Easy Access, os menus pra acessar os programas ou transações, mas na prática ninguém usa isso, todo mundo sabe o código da transação e digita direto. E código de transação é um acrônimo em alemão, SE80 por exemplo é o Object Explorer.</p>
<p>Sobre esse runtime você tem as ferramentas de desenvolvimento, o ABAP Workbench que é o conjunto de ferramentas como editor, dicionário de dados, painter de tela e tudo mais, que é como se fosse seu Visual Studio ou Eclipse de hoje. Falando em Eclipse existe até o ADT que é o ABAP Development Tools que são plugins pro Eclipse, pouca gente usa, por incompatibilidade de servidores, funcionalidades ou por serem reacionários a mudanças e além de demandar uma curva de aprendizado..</p>
<p>Como mencionei antes, os programas são chamados por transações no SAP, o mesmo conceito de shortcuts do windows. E pra mim a coisa mais importante nessa infraestrutura é o Dicionário de Dados (tela) que contém todos os metadados do sistema que você acessa via o Object Navigator ou transaction SE80. Todas as transações são identificadas por um código como esse, é tipo a URL pros programas.</p>
<p>Vou tentar resumir bastante agora, mas por exemplo, no dicionário você tem tabelas. Mas você lembra que eu falei que o Basis abstrai o banco por baixo? Uma tabela em ABAP é uma representação 1 pra 1 com uma tabela no banco se ela for uma tabela que chamamos de &ldquo;transparente&rdquo; mas existem &ldquo;pools&rdquo; que são entidades independentes no dicionário mas agrupados em tabelas físicas maiores ou &ldquo;pools&rdquo; no nível do banco de dados. Você tem cluster tables que são fisicamente agrupadas em clusters baseados em suas chaves primárias, são alternativas para limitações de banco de dados, por exemplo, um tamanho de registro maximo ou de número de campos. Os dados de um registro lógico é guardado em mais do que uma linha no banco de dados, fora as chaves os demais campos ficam serializados em um campo raw. Obviamente com o pior conceito de performance, mas de novo, fica transparente para o workbench de abap o mesmo select. O trade-off para isto é a criação de tabelas secundárias particionadas. não lembro se na prática funcionam como views materializadas em bancos como postgresql.</p>
<p>Mais interessante são os Domains, que armazenam o registro dos elementos de dados. Acho muito engraçado que muito pouca gente sabe que qualquer banco de dados relacional que se preza tem coisas como User-defined types ou custom types. Por exemplo, se eu falo vamos representar um preço numa tabela, a primeira coisa que um programador pensa é: vou criar um campo FLOAT ou INT. No SAP você tem um tipo chamado PRICE e toda tabela que precisa de preço vai ter o tipo PRICE e ponto. Se amanhã o tipo primitivo de PRICE mudar, ela muda pra todo o sistema replicando a alteração para todos os demais objetos dependentes.</p>
<p>Mais ainda, internamente no ABAP digamos que eu queira uma variável no programa pra armazenar o preço que eu puxo de uma tabela. Que tipo vai ser essa variável? Vai ser tipo PRICE, igual o que vem da tabela. E como ABAP compila tipo uma AST mas sua execução é dinâmica, se o tipo PRICE muda, não só as tabelas, mas os programas passam a usar o tipo novo, é plug and play assim. Você acha que já viu boas modelagens de dados, mas comparado com o que se tem numa instalação SAP, chega a ser primitivo como um hello world.</p>
<p>Sendo ainda mais específico, uma tabela tem campos, campos tem elementos de dados, elementos de dados tem domínios, e o domínio contém o tipo de dado para armazenamento de banco de dados, valores permitidos, rotinas de conversão. Por exemplo, o elemento Centro de Custo valor string &ldquo;1000&rdquo; é convertido pra &ldquo;0000001000&rdquo;</p>
<p>então se um usuário preencher &ldquo;0001000&rdquo; ou &ldquo;01000&rdquo;</p>
<p>vai ser tudo reduzido a exatamente o mesmo valor. Então o elemento de dados já traz embutido não só o tipo primitivo mas regras como validação, conversão e inclusive a internacionalização do nome. Basta você falar que o campo é determinado domínio e ele já herda tudo.</p>
<p>Uma aplicação prática:
Um campo pro exemplos, o domínio email: os espaços no início e no fim são suprimidos, o campo é convertido para minúsculo, implementa-se uma regra de validação tipo regex em ABAP e um tamanho máximo.
Qualquer campo que seja exibido na tela do usuário já irá implementar todas estas regras.
Caso um dia o campo precise ser ampliado ou implementar uma regra do tipo @exemplo.com não é mais permitido, TODAS as referências utilizadas são automaticamente herdadas sem a necessidade de search, copy &amp; paste, etc.</p>
<p>&mdash;&gt; Foi o primeiro tapa na minha cara, onde eu aprendi que dar manutenção em programas é muito mais difícil do que escrever programas. E a SAP criou um fluxo de trabalho extremamente eficiente. Permitindo um código base existir e evoluir por quase meio século.</p>
<p>Eu falei de localização antes, e aqui entra domínios e elementos de dados de novo.
Pra quem acha que localização é só traduzir texto na verdade estamos falando de todos os cenários e regras de negócio particulares de um país para atender as necessidades fiscais e contábeis, por exemplo livros fiscais, cálculo e contabilização de impostos, retenção de impostos, obrigações acessórias como SPED contábil, contribuições, REINF, além de cálculo de folha de pagamentos e outras regras derivadas de CLT por exemplo.</p>
<p>Como eu disse que existe essa abstração de elemento de dados, num designer de tela, como você veria num Visual Basic, você não precisa arrastar um Label, editar o texto e daí arrastar um campo de texto e criar as validações. Você arrasta o elemento de dados inteiro e ele já vem certinho o texto internacionalizado, o tipo de campo de input correto e todas as validações e regras de negócio relacionados. É um nível acima do que você imagina como Rapid Application Development ou RAD que um Visual Basic ou Delphi nunca atingiram.</p>
<p>Pois é, lembra que eu falei que nomes de tabelas e campos no banco são tudo acrônimos em alemão? Não em importância porque graças aos elementos de dados no dicionário, cada campo além de tudo tem as traduções nas diversas línguas suportadas, então todas as telas traduzem automaticamente também.</p>
<p>Eu acho interessante porque se você começar a olhar todos os componentes em detalhes, é absolutamente surpreendente que um sistema como esse existe, e faz você, enquanto programador, enxergar o quanto nós somos ruins. Por exemplo, um programador amador, se precisasse fazer um sistema de vendas, começaria pensando um troço besta como uma tabela de produtos, que tem campos como código do produto, nome, quantidade e preço.</p>
<p>Esse é obviamente o jeito idiota de se fazer. Se você for um pouco mais avançado, vai pensar em variante de produtos, SKUs e modificadores de preço como descontos, preço de campanha.</p>
<p>Mas se você for no módulo de vendas do SAP, o famoso SD, você vai ter um sistema de precificação que é uma matriz multidimensional programática que leva em conta as variantes todas (que por si só são outras matrizes) e todos os aspectos geográficos, como setor de atividade, centro de distribuição, e tantos outros parâmetros que modificam desde custo até imposto que vai na composição dos preços. Um produto na verdade são N produtos que custam M preços diferentes dependente de quem faz o pedido, pra onde entrega, como foi manufaturado ou comprado, onde está armazenado, em que cidade, em que estado, de que país.</p>
<p>Eu tive a oportunidade de lidar com diversos módulos do R/3, incluindo sales and distribution ou SD, materials management ou MM, projects ou PS, financials ou FI, human resources ou HR. Como eu disse, eu não era um consultor funcional, eu era um integrador, principalmente pra sistemas Web. O R/3 tinha o equivalente a RPC pra integração ou Remote Procedure Calls, se você for de Java pense em algo como RMI ou Remote Method Invocation, pense a época de DCOM, COM+ ou CORBA. Tudo tecnologia pré-JSON-RPC que temos hoje em Web.</p>
<p>Na minha época, a SAP começou a tentar modernizar tudo pra Web, seguindo a tendência do mundo Java. A SAP compra muitas empresas o tempo todo, o portfólio de aquisição deles é enorme. Uma das empresas foi a In-Q-My de outra figura que ficou muito famosa na época, o Shai Agassi, empreendedor israelense. Muito antes de Elon Musk era Shai Agassi que estava iniciando a tendência de carros elétricos com sua empresa Better Place, que desenvolvia baterias pra carros elétricos e foi fundada em 2007, faliu em 2013, mas pense que foi tipo um pré-Tesla. Acho que meio por isso eu também não fiquei muito surpreso com um Elon Musk tendo conhecido Shai Agassi antes. Com a aquisição ele virou presidente de produtos da SAP e foi um forte candidato a suceder Henning Kagermann como CEO em 2007. Mas nessa batalha o Kagermann se estendeu como CEO até 2009 e Agassi renunciou.</p>
<p>Sob a liderança de Agassi surgiu a plataforma SAP NetWeaver, um pacote complementar ao ERP R/3, que adicionava componentes Web ao legado corporativo. Isso incluiu pacotes como o SAP Enterprise Portals, que foi onde eu me certifiquei e virei especialista. Era um conjunto de aplicações J2EE que rodava no servidor de aplicações da In-Q-My. Diversos outros produtos foram saindo nessa época, os xApps, o Exchange Infrastruture ou XI que era o sistema de filas, a versão menor do ERP pra empresas menores que foi o Business One e muito mais. Foi uma pequena revolução e todo mundo ficava confuso porque era o mundo legado recebendo a porrada do mundo Web. E exatamente nesse momento um cara como eu, que era de Web e aprendendo o legado, virei mosca branca.</p>
<p>Nessa época estávamos entrando na versão 4.7 do R/3. Quando eu estava saindo do mundo SAP eles estavam mudando o nome do produto principal de ERP de novo. De R/3 passou a ser ECC ou Enterprise Core Components. Então hoje em dia você não ouve mais falar de R/3 e sim de ECC.</p>
<p>Além disso eu falei que o Hasso Platner e o Larry Ellison não se bicam, mas uma das combinações mais fortes do mundo Enterprise sempre foi o R/3 rodando com banco de dados Oracle. Era bem bizarro, é meio como é hoje os iPhones da Apple usando telas OLED da Samsung embora eles sejam concorrentes. Hoje o cliente que gerar maior receita de banco de dados oracle no mundo é a própria SAP, que vende sua licença SAP adicionada do custo de licenciamento que é repassado para a Oracle. Houve uma litígio jurídico entre ambas sobre o valor do repasse, a primeira sentença foi de 1,3 Bilhões de USD.</p>
<p>O começo da sua história a SAP não lidou com bancos próprios, sendo como princípio fundamental multiplataforma e multi-banco. Para mitigar esta dependência uma das alternativas foi o famoso ADABAS D, que eles liberaram como open source com licença GPL no ano 2000. Em 2003 a SAP AG e a MySQL AB se uniram e lançaram um novo banco próprio pra SAP chamado de MaxDB. A MySQL saiu da jogada depois de alguns anos e a SAP continuou sozinha com o MaxDB que hoje é closed source. Para “controlar” a dependência sobre o sistema operacional a SAP é uma das acionistas da(o) Suse. Hoje o caminho da SAP é todo direcionado para o SAP HANA que é o novo banco de dados que roda como núcleo dos produtos de inteligência da SAP, é como se fosse um mantra ou uma lavagem celebral, tudo hoje na SAP gira em torno do Hana.</p>
<p>SAP HANA é um produto que vale a pena dar uma olhada, em resumo ele é um banco de dados in-memory (pensa um Redis), orientado a colunas (pensa um Cassandra) e com duas coisas absolutamente importantes: suporte a OLTP e OLAP.
OLTP pense em transações de bancos relacionais: a integridade dos dados é absolutamente importante num sistema enterprise.
E OLAP é a parte analítica, que é suporte a análise multi-dimensional, que é o caso pra Big Data, Data Mining, Data Warehouse. Se você nunca ouviu falar em dimensões, modelo estrela, cubos, não sabe o que é análise de dados ainda. Você acha que com uma mísera query SQL vai conseguir chegar de forma eficiente nos dados de uma multinacional tipo uma Nestlé e agregar os resultados de vendas de todos os países do mundo de uma só vez? Com capacidade de dar drill down do faturamento anual da marca e ir descendo - drill down - até chegar no faturamento de uma loja? Pensa no que significa esse tipo de query, e agora pensa centenas delas pra ter a visão do MUNDO de uma multinacional. Quando virar data scientist não era hipster como é hoje, já se lidava com Big Data em multinacionais.</p>
<p>O S/4 HANA é o primeiro re-code da SAP. Onde o antiquado, combalido mas ativo e forte cinquentão R/3 e suas repaginações segue finalmente para o caminho de legado.
O trocadilho S/4 é proposital da evolução do R para S e do 3 para 4.</p>
<p>Por muitos anos os sistemas SAP foram especializados em lidar com grande quantidade de transações. Vendas, compras, contratos, projetos, manufatura, etc. Onde a prioridade foi funcionalidade com o tradeoff de performance.  Agora que as empresas que implementaram tem essa quantidade massiva de dados, o foco tem sido em tornar esse dados inteligentes. Já que tem todos os dados vitais de uma empresa, porque o sistema já não pode tomar muitas decisões estratégicas? Daí entra o HANA ligado a diversos outros produtos como o TREX o próprio Hadoop e assim por diante.</p>
<p>Voltando a falar do mercado nacional, eu lembro que quando comecei no mercado SAP o mercado top tier era altamente concentrado no que chamávamos de Big Five. Pra explicar, haviam as grandes consultorias de renome que eram quem conseguia fechar contratos gigantes como Petrobrás, Vale. E existiam as quarteirizadas menores como onde eu trabalhava que prestavam serviço via essas grandes. No começo do século as Big Five eram IBM, Arthur Andersen, Price WaterhouseCoopers, Ernst &amp; Young e KPMG.</p>
<p>Mas não demorou muito e a parte de consultoria Arthur Andersen virou Accenture, a Price foi comprada pela IBM, a Ernst &amp; Young foi comprada pela Capgemini. No fim as Big 5 foram consolidadas mais ainda em Big 3.</p>
<p>Digamos que você quer se tornar um consultor, o que fazer? Você precisa passar pela academia, são os cursos de certificação licenciados pela SAP. Existem dois motivos de porque você não tem como ser autodidata no mundo SAP. Primeiro, tenta ir no Google, você não vai achar nada de muito útil online. Toda a base de conhecimento da SAP é fechada, só consultores certificados tem acesso a essa base. Existe tipo uma Deep Web da SAP que ninguém tem acesso, que é a SCN que mencionei no começo.</p>
<p>Fora isso pra acessar essa base e também pra conseguir acessar o sistema das empresas, você tem um SAP ID que é seu RG de consultor e é reconhecido mundialmente e cadastrado na SAP da Alemanha. Sem isso você não tem autorização pra atuar como consultor. Parece que diminuiu um pouco a dificuldade, antes você precisava fazer a academia, hoje tem um tal de Certification Day. Fragmentou um pouco o conceito de academias e bootcamps, hoje tem mais cursos não-oficiais pra assuntos específicos. A época de ouro da SAP no Brasil foi do meio dos anos 90 até o começo dos 2000 eu acho, quando as taxa hora eram exorbitantes. Mas até esse mercado se comoditizou um pouco hoje em dia, mas a exclusividade ainda garante boas taxas.</p>
<p>A certificação é do consultor, e ligado à consultoria que pagou pelo curso ou prova. E são cursos caros. Na minha época acho que uma academia era na casa dos 10 ou 20 mil reais. Pra consultoria isso é importante porque ela precisa de um número mínimo de consultores ligados ao seu CNPJ. Se o consultor trocar de consultoria ele precisa pedir a transferência. É uma burocracia da SAP que ainda mantém um certo controle de como organiza seu mercado. É meio parecido com o que a Microsoft tem com seus MCPs ou MCSEs.</p>
<p>Em começo de carreira o melhor é iniciar como trainee de uma das Big que mencionei, entrar no programa da IBM, Accenture, Deloitte, Capgemini e outras. Elas são pouco divulgadas então tem que cavar um pouco. E não é só pra programadores, na verdade os consultores funcionais são especializados em cada módulo de negócio. Um contador pode virar consultor do módulo financeiro, alguém que se formou em direito trabalhista pode trabalhar no módulo de HR, por exemplo.</p>
<p>Outro ponto que eu achava interessante antigamente era o que a gente coloquialmente chamava de Z/3. Uma das convenções de programação é que tudo que você desenvolvia em ABAP que não era standard a gente começava o nome com Y ou Z, muito mais Z. E um dos principais problemas de consultorias ruins é que em vez de resolver os problemas do jeito certo, via customização dos módulos funcionais, eles resolvem fazer via força-bruta, basicamente um copy e paste de uma transação que já existe em um Z pra mudar duas linhas. Que nem antigamente quem fazia web tosco tinha index.php e index2.php ou coisas assim.</p>
<p>Faça isso por muito tempo e você basicamente perde a habilidade de conseguir fazer upgrade do sistema porque as transações originais vão ser atualizadas, mas as cópias Z vão ficar pra trás. Então se formou um mercado paralelo pra dar manutenção nesses malditos Z/3. E você acha que só empresa pequena que faz isso, não empresas grandes fazem Z demais também. Eu vi vários.</p>
<p>Pra evitar esse cenários, depois da minha época a SAP inventou uma forma melhor pra estender os programas padrão sem ter que clonar em Zs, e isso é chamado de SAP Fiori ou SAP na Web. Eu mesmo ainda não vi como isso funciona, mas deve ajudar a evitar as dores de cabeça de manter um Z/3.</p>
<p>E falando em suporte, um dos grandes mercados pra consultorias de SAP ou Oracle é a parte de suporte. Eles vendem horas de suporte pra grandes empresas. Na minha época não tinha mas agora tem opções como a Rimini Street.</p>
<p>Fazer tudo by the book custa caro. Mas na prática não existe nenhuma trava de funcionamento do sistema, como chave de ativação ou algo assim. E mesmo que tivesse daria pra bypassar porque o código fonte do sistema em ABAP vem no sistema, ele é modificável. Mas só isso não é suficiente, sem o suporte da SAP seu sistema deteriora.</p>
<p>Mas em vez de pagar os 22% de valor de aquisição da licença pra efeito de manutenção muitas empresas optam por não evoluir o SAP, porque os processos em si ficam estáveis e não mudam tanto. Os demais gaps de desenvolvimento eles contratam de suporte alternativo, literalmente mercado negro mesmo. Fica a cargo de empresas como a Remini Street manutenções desse tipo.</p>
<p>Durante minha carreira enterprise eu passei por diversas empresas diferentes. Eu mencionei como ganhei o projeto de SAP Enterprise Portals na Suzano Papel e Celulose no meu vídeo de O que Estudar, mas além disso eu passei quarteirizado por consultorias como Atos Origin, e participei do projeto de merge de telecoms que gerou a Vivo, depois o merge que gerou a Claro, Ultrafértil Fosfértil, C&amp;A, Copersucar, ESAB, Embratel, e outras.</p>
<p>Apesar de ter sido um dos períodos mais estressantes foi também um dos mais interessantes. O trabalho era longe de ser glamuroso. Absolutamente longe de qualquer ideal de tech startup que você já tenha visto, na verdade acho que o total oposto. Não tem hora. Não tem lugar. Uma vez eu estava na Atos Origin falando de não sei que projeto e literalmente numa conversa de bebedouro troquei idéia com um gerente de não sei aonde, e ele falou de um problema que estava tendo no módulo de viagens na instalação da Embratel e, por acaso, eu tinha uma suspeita do que podia ser o problema. Ele disse que ninguém estava conseguindo resolver e quando falei da minha suspeita ele só disse: “porra, você pode ir pro Rio amanhã?” E assim eu fui pro Rio, passei 3 dias e resolvi o problema.</p>
<p>Só pra entender qual era o problema, uma transação SAP tem tela gráfica. Mas ela pode rodar headless. Pensa como rodamos hoje um Selenium, Capybara, com o Chrome headless. Mesma coisa. Eles chamam isso de Job, que roda em background. Agora imagina se uma tela headless de repente sobe um dialog box que não tem como dar cancel, daí obviamente o Job trava. Ninguém parou pra pensar nisso, daí eu achei onde abria dialog, não lembro se fiz algum ABAP uma exit ou algo assim pra tirar o dialog e aí tudo voltou a passar.</p>
<p>Um bom consultor, por definição, tem que pensar fora da caixa, como eu disse, nós temos menos tempo e menos recursos que todo mundo que tentou resolver antes da gente. Por isso que quando um consultor mosca branca troca de consultoria até o cliente tende a mudar de consultoria se precisar pra não perder o consultor. Nas consultorias menores, o consultor é mais importante do que a a consultoria. Isso eu sempre achei legal. Por outro lado gente que reclama demais nunca vai ser um solucionador de problemas - óbvio porque claramente gasta mais tempo reclamando que resolvendo - e, portanto, nunca será consultor porque nós éramos a elite, a swat, os navy seals, quando nos chamam é porque a casa já caiu. Quer trabalho facinho, vai trabalhar em fábrica.</p>
<p>Mas, uma coisa é certa, nenhum programador tem como saber as regras de negócio de grandes empresas sem ter passado por elas, de preferência por várias delas. Eu não sou especialista, mas ter tido a oportunidade de lidar com áreas completamente diferentes como vendas, recursos humanos, financeiro, manufatura, logística, controladoria, me deu uma visão de estrutura de empresas que nenhum MBA, e obviamente, nenhum evento ou workshop ou mentoria de startup jamais vai conseguir dar pra ninguém. Você precisa ter sofrido o mundo enterprise pra entender o mundo enterprise, é impossível saber disso de outra forma.</p>
<p>Como tangente eu acho engraçado quando o recém-formado ou mesmo drop-out “acha” que vai revolucionar qualquer coisa. Não vai. É uma loteria, é sorte, 1 em milhares. A razão é simples: o moleque chega e diz “vou revolucionar o mercado médico”, aí eu pergunto, beleza, quanto tempo você trabalhou na medicina? Em que especialidades? Em quantos hospitais? E ele fala “nunca fui médico”. Eu só balanço a cabeça e mando embora. Nem os próprios médicos entendem o sistema onde eles mesmos trabalham, estando dentro. Ou você tem um sócio médico que compreende os processos, ou esquece.</p>
<p>E não tente se comparar com os grandes. Você não é o Reid Hoffman. Você não é o Jack Ma. Você não é o Elon Musk nem o Shai Agassi. É um absurdo querer se comparar com eles.</p>
<p>Muitos dos processos que eu apliquei nas empresas seguintes que trabalhei, incluindo na minha própria, são derivados do que eu aprendi no mundo SAP. As partes boas eu trouxe, as que eu vi na prática que não funcionavam eu mudei, mas não saiu a idéia do zero da minha cabeça. Foram anos e anos tentando de tudo, falhando, aprendendo porque falhou e tentando de novo. E eu tive a oportunidade de fazer isso em grandes empresas. E mesmo com tudo isso quando comecei minha própria empresa tive problemas. Imagine você começar do absoluto zero e no risco? É como dizer “vou atravessar o Atlântico de caiaque, mas nem sei nadar ainda”. Minha cabeça explode ouvindo estupidez desse nível.</p>
<p>O mundo SAP serviu pra amadurecer minha visão de negócios, corporações e o mercado como um todo, uma nova dimensão que um programador recebendo ordem de serviço numa fábrica jamais vai ter. Minha recomendação é que se você tiver a oportunidade, tente, não vai perder nada. Se eu pudesse voltar no tempo será que eu passaria por todo esse stress de novo? Com certeza!</p>
<p>E é isso aí, hoje foi uma pincelada muito rápida no que eu aprendi em 5 anos nesse mercado, não dá pra detalhar tudo mas acho que isso deve dar uma visão geral, pelo menos uma visão geral segundo o que eu experimentei, tem muito mais que nem eu sei ainda. E vocês, já tiveram experiência no grande mercado enterprise? Compartilhem suas experiências nos comentários abaixo. Se curtiu mandem um joinha, compartilhem o vídeo, assine o canal e clique no sininho. A gente se vê, até mais.</p>
]]></content:encoded><category>negocios</category><category>arquitetura-de-software</category><category>programacao</category><category>akitando</category></item><item><title>[Akitando] #25 - Sua Linguagem É Especial? Parte 2 em 2001</title><link>https://akitaonrails.github.io/2018/11/08/akitando-25-sua-linguagem-e-especial-parte-2-em-2001/</link><guid isPermaLink="true">https://akitaonrails.github.io/2018/11/08/akitando-25-sua-linguagem-e-especial-parte-2-em-2001/</guid><pubDate>Thu, 08 Nov 2018 17:00:00 -0200</pubDate><description>&lt;p&gt;Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal &lt;a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener"&gt;Akitando&lt;/a&gt;. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.&lt;/p&gt;
&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/O4CWVQLbi48"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;h3&gt;Descrição no YouTube&lt;span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"&gt;&lt;/span&gt;
&lt;a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Recapitulando, na série &amp;ldquo;Sua Linguagem NÃO é especial&amp;rdquo; expliquei porque tudo que você acha que é diferente e moderno na sua linguagem já existia décadas atrás.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal <a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener">Akitando</a>. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/O4CWVQLbi48"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<h3>Descrição no YouTube<span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"></span>
    <a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Recapitulando, na série &ldquo;Sua Linguagem NÃO é especial&rdquo; expliquei porque tudo que você acha que é diferente e moderno na sua linguagem já existia décadas atrás.</p>
<p>No episódio anterioe eu comecei  a explorar um pouco o outro lado: ninguém disse que você não pode gostar da sua linguagem e, mais importante, dominá-la até o fim.</p>
<p>Veja o que eu fazia quando estava no começo da minha carreira e compare com o que você faz hoje em dia.</p>
<p>Em particular, veja como exploração sem limites pode levar você a antecipar tendências, sem querer.</p>
<p>Quer ver como fazíamos &ldquo;Ajax&rdquo; e &ldquo;NoSQL&rdquo; já em 2001?  :-D</p>
<p>Links de referência:</p>
<ul>
<li>ASP XML Framework (<a href="https://github.com/akitaonrails/ASP-Classic-XML-NoSQL"target="_blank" rel="noopener">https://github.com/akitaonrails/ASP-Classic-XML-NoSQL</a>)</li>
<li>Nested Set (<a href="https://en.wikipedia.org/wiki/Nested_set_model"target="_blank" rel="noopener">https://en.wikipedia.org/wiki/Nested_set_model</a>)</li>
</ul>
<h2>Script<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>No episódio passado eu mostrei um mini-framework que eu fiz com ASP clássico tentando imitar o que, na época, era o moderno ASP.NET Web Forms. Estamos falando de 2001.</p>
<p>Mas alguns meses antes desse framework eu fiz um código que - olhando hoje - é ao mesmo tempo assustador - porque o código é grande e bem feio - mas também surpreendente.</p>
<p>Lembram quando eu falei no episódio de Blockchain rapidamente sobre o Hype do NoSQL em 2010? E se eu te disser que eu já usava noSQL em 2001?</p>
<p>(&hellip;)</p>
<p>Estamos em 2001, como eu expliquei no episódio anterior eu estava desenvolvendo e dando manutenção em diversos sites esportivos de propriedade da PSN como os sites do Corinthians e Cruzeiro aqui no Brasil.</p>
<p>Além disso havia todo um Gerenciador de Conteúdo usado pelos jornalistas pra postar artigos e todo workflow de aprovação e publicação. Tinha área pra subir fotos, ferramentas pra montar pesquisas online tipo polls e muito mais.</p>
<p>Mas tinha 2 problemas que eu queria resolver. Um deles é que antigamente, tanto em ASP ou PHP a gente fazia o HTML misturado com o código. Se você já abriu o código fonte de um Wordpress, bom, era pior que isso.</p>
<p>Olha um exemplo disso, (aponta pro lado) HTML, dados, styles (já tinha CSS mas ele não era tão poderoso como hoje e pouca gente sabia usar direito ainda) e código tudo misturado. Era uma zona.</p>
<p>Uma coisa que tava começando a se popularizar na época era XML. Mas nem todo mundo entendia isso direito ainda. Muito menos no mundo ASP. Assim como hoje em dia pra vocês é tudo JSON, pra mim naquela época o quente era tudo XML.
Tecnologia funciona assim, você não sabe os limites, vai testando.</p>
<p>Então o primeiro problema que eu queria resolver era separar os dados do HTML. Eu não lembro se já existia o framework de Java Apache Coccoon, mas pra quem não sabe ele tinha exatamente esse conceito. Pra quem não sabe o que é XSL é o Extensible Style Language. Hoje XSL é uma família, por exemplo você talvez já tenha ouvido falar de XPATH? Faz parte.</p>
<p>Na época só existia o XSLT que é um padrão de transformação pra modificar um XML em outro XML. Então eu pensei, ora, um exemplo de XML é HTML ou mais especificamente o XHTML que a gente usava na época. Um HTML mais restrito e que é um XML válido. Pensei, porque não separar os dados puros em um XML estruturado e usar transformações XSLT pra converter em HTML?</p>
<p>E foi o que eu fiz. Já que todo site de evento esportivo era basicamente o mesmo template mas com dados diferentes, eu reduzi os dados de cada evento em XML e usava essas transformações pra gerar o HTML final a partir de um template XSLT.</p>
<p>Vou falar que o código que eu fiz ficou tão complicado que só de bater o olho hoje rapidamente nem eu consigo entender tudo que eu fiz. (risos) Quem me conhece sabe que eu costumo dizer que coisas como testes unitários não é uma coisa que você faz pros outros, é pra você mesmo de amanhã. E eu digo essas coisas por experiência, se eu pudesse voltar 17 anos no passado eu daria um tapa na orelha em mim mesmo.
Mas dá um desconto, em 2001, o assunto testes automatizados era meio novo até em Java. Só pra vocês terem uma idéia o site do JUnit só foi lançado 1 ano antes, em 2000.</p>
<p>De qualquer maneira eu meio que consegui fazer isso e pelo menos alguns eventos como Campeonato Paulista, Copa Libertadores, Copa Merconorte, Copa Mercosul, eu acho que consegui implementar nesse framework.</p>
<p>Novamente, eu fiz componentes VBScript encapsulados em Windows Script Components ou WSC. Eu criei componentes com nomes meio bizarros como TXMLSynchro,
TXMLFront, TXMLEvents,
TXMLLanguage e TXMLTranslation.</p>
<p>Ou seja, como eram portais de conteúdo internacionais pra América Latina e Estados Unidos, eu também tinha uma mini-engine de internacionalização. E suportando 2 bancos de dados, SQL Server e Oracle.</p>
<p>E porque tudo começa com T? Porque eu era desenvolvedor de Delphi alguns anos antes. E em Delphi todos os componentes começavam com a letra T e eu acabei pegando esse hábito de começar tudo com T. Em Delphi existia essa convenção porque tudo que definia um Tipo começava com T.</p>
<p>O componente principal TXMLSynchro usava uma convenção de pastas e ele ia percorrendo as pastas em busca dos XML e os templates XSL pra ir gerando os HTML. Esse conceito soa familiar? Se você já usou Jekyll por exemplo, é parecido, só que em vez de ser Markdown, era XML. Então eu tinha um gerador de site em XML em 2001.</p>
<p>Na verdade, era um passo além. Os jornalistas podiam editar o conteúdo via um Admin que gravava o XML no banco de dados. Daí eu pegava esse XML e aí convertia pra HTML via o XSL, por isso era TXML Synchro o nome do componente.</p>
<p>Ao mesmo tempo esse pacote era um gerador de eventos, daí o outro componente TXML Event, daí cada nova Copa ou evento era mais ou menos simples de gerar tudo.</p>
<p>Agora vem o principal. Eu gostei desse conceito de gravar XML natabela. E fui um passo além. Tinha um outro problema em todos os eventos esportivos, todo campeonato era visualizado via uma chave. Sabe? Basicamente uma árvore de nós convergindo na semi-final, final até o campeão.</p>
<p>Mas todo evento tem chaves diferentes. O problema: como eu represento isso num sistema multi-eventos?</p>
<p>A primeira coisa que você provavelmente teria vontade de fazer é criar uma tabela Evento, uma tabela Jogo e uma tabela Time ou algo assim e ir criando uma linha no banco pra cada nó da árvore. Isso é um design terrível. É o pattern que chamamos de Parent-Child. Terrível porque não tem como trazer todos os nós da árvore num único select.</p>
<p>Árvore é uma das coisas ruins de se mapear numa tabela SQL. O jeito correto é usar um Nested Set, vou deixar o link abaixo se você nunca ouviu falar em Nested Sets. Mas em 2001 eu também não conhecia isso, só conhecia o Parent-Child que é o que eu falei agora que é ruim, e pelo menos eu sabia disso.</p>
<p>Aqui foi um insight. Gerenciar nós de uma árvore, em SQL não é trivial. Gerenciar nós num XML era fácil, é um DOM, como o DOM que você manipula hoje via Javascript num HTML, é uma árvore. CreateElement, Sibling, Child, etc. Mas não existe banco de dados XML, não em 2001.</p>
<p>Mas, bancos SQL tem campo BLOB. E se eu simplesmente gravar o XML num campo blob? Daí com um único select eu puxo o XML inteiro que representa a árvore toda e posso passar num DOM Document pra parsear e criar os objetos todos.</p>
<p>E se eu for além, e se eu usar XSL em cima do XML que veio do banco pra gerar o HTML final que o jornalista ia ver no gerenciador de conteúdo?</p>
<p>E se eu for além, e se eu usar esse troço novo que o Internet Explorer 4 tinha inventado uns 3 anos antes chamado Dynamic HTML que me permite modificar o HTML dinamicamente na página via Javascript? Cês tão vendo? Eu to falando de tecnologia de ponta aqui.</p>
<p>E se eu somar isso a esse componente novo chamado MSXML que eu podia instalar tanto no servidor mas tinha componente pro browser. Isso permite, via Javascript ou VBScript, fazer uma requisição HTTP pra aplicação ASP me devolver o XML e, no client-side, eu poderia via javascript parsear com MSXML e ter o DOM na memória?</p>
<p>Agora se eu somar tudo isso. Eu posso fazer o jornalista clicar em qualquer nó da árvore, editar o nome do time, pontuação e tudo mais direto na representação do DOM do XML na memória do browser. Daí via Javascript gerar o XML que representa essa árvore e dar POST pro servidor, que daí ia gravar direto na tabela do banco.</p>
<p>O que eu tinha acabado de fazer?</p>
<p>Asynchronous Javascript and XML ou AJAX, nome que seria cunhado só em 2005, 4 anos depois. Eu não fui o primeiro a fazer isso, muito antes do Google fazer o Gmail que popularizou a técnica, a Microsoft já tinha o Outlook Web App em 2000 que era basicamente um Outlook versão Web que usava o componente XMLHTTP, que é o precursor do XMLHttpRequest. Ninguém usa esse objeto direto, hoje em dia vocês usam o que? Axios? Mas por baixo é o mesmo XMLHttpRequest. Esse componente nasceu pela primeira vez em 1997 ou 98 com o Internet Explorer 4, mas acho que só começou a ser usado de verdade com o IE 5 em 1999.</p>
<p>E isso de serializar e deserializar árvores de objetos XML num campo blob numa tabela é um tipo rudimentar de NoSQL. É o que hoje a gente faz com Postgres e o tipo de campo JSONB. É basicamente a mesma coisa: em vez de fazer um monte de tabela pra cada campeonato, eu reduzi tudo numa tabela só sem schema, schemaless.</p>
<p>O termo NoSQL foi cunhado primeiro em 1998 mas só se popularizou quando a Last.fm reintroduziu o termo em 2009, 8 anos depois do que eu fiz.</p>
<p>Imaginem minha surpresa quando em 2005 eu vi todo mundo ficando doido com o conceito de Ajax e em 2009 todo mundo ficando doido com o conceito de NoSQL. Eu juro que coçava a cabeça pensando, é legal, mas isso é velho não?</p>
<p>Lembrando que em 2001 não tinha ainda Hacker News, nem Reddit, nem Quora, nem Stackoveflow. Esses conceitos nasceram organicamente combinando as peças que eu aprendi fora da caixa e fazendo o que todo programador novo faz: over engineering.</p>
<p>Engraçado que eu tinha noção que a solução que eu fiz era bem overkill e over engineering mesmo pra época, mas assim como muitos de vocês, é aquelas coisas que você faz mais porque é divertido e menos porque é um bom custo-benefício.</p>
<p>O objetivo dessa história é ilustrar como sem querer eu cheguei nas mesmas conclusões que outras pessoas chegaram de outra forma. Acho que muitas outras pessoas usavam da mesma maneira, mas não era tão famoso porque não tinha blogs, não tinha meetups, não tinha screencasts nem nada disso em 2001. Se você não prestar tanta atenção na propaganda, ou a forma como os outros dizem que você deve usar essa ou aquela ferramenta e encarar tudo como peça de lego eventualmente você mesmo vai chegar nas mesmas conclusões que os grandes nomes chegam.</p>
<p>O que eu falo mais é o seguinte: se você está seguindo o que os outros estão dizendo, você já está atrasado. Eu não sigo muito os outros, eu penso: pra que esse troço serve ou não serve. Em vez de seguir o que os outros dizem “ah, isso só dá pra fazer se for em Java” ou “ah, isso o melhor é se for em Javascript”. Eu penso. Quem disse? Deixa eu testar. 99.9% das vezes não funciona, mas essa é justamente a graça do negócio. Programação não é pra ser um negócio que todo código que vc digita precisa necessariamente ser útil pra alguma coisa. É como dizer que toda vez que você for chutar pro gol precisa fazer gol. Senão nem vale a pena tentar chutar. Se futebol fosse só isso, não teria graça, teria? E fazendo assim, um vez por década você tem um momento eureka!</p>
<p>E você, já teve seu momento eureka em algum momento? Compartilhe com a gente sua história nos comentários abaixo. Se curtiram o vídeo mandem um joinha, assinem o canal e cliquem no sininho! A gente se vê, até mais!</p>
]]></content:encoded><category>desenvolvimento-web</category><category>arquitetura-de-software</category><category>linguagens-de-programacao</category><category>akitando</category></item><item><title>[Akitando] #24 - Sua Linguagem É Especial? Parte 1 em 2001</title><link>https://akitaonrails.github.io/2018/11/06/akitando-24-sua-linguagem-e-especial-parte-1-em-2001/</link><guid isPermaLink="true">https://akitaonrails.github.io/2018/11/06/akitando-24-sua-linguagem-e-especial-parte-1-em-2001/</guid><pubDate>Tue, 06 Nov 2018 17:00:00 -0200</pubDate><description>&lt;p&gt;Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal &lt;a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener"&gt;Akitando&lt;/a&gt;. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.&lt;/p&gt;
&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/NwAvovzHRDU"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;h3&gt;Descrição no YouTube&lt;span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"&gt;&lt;/span&gt;
&lt;a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Muito bem, na série &amp;ldquo;Sua Linguagem NÃO é especial&amp;rdquo; expliquei porque tudo que você acha que é diferente e moderno na sua linguagem já existia décadas atrás.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Disclaimer: esta série de posts são transcripts diretos dos scripts usados em cada video do canal <a href="https://www.youtube.com/channel/UCib793mnUOhWymCh2VJKplQ"target="_blank" rel="noopener">Akitando</a>. O texto tem erros de português mas é porque estou apenas publicando exatamente como foi usado pra gravar o video, perdoem os errinhos.</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/NwAvovzHRDU"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<h3>Descrição no YouTube<span class="hx:absolute hx:-mt-20" id="descrição-no-youtube"></span>
    <a href="#descri%c3%a7%c3%a3o-no-youtube" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Muito bem, na série &ldquo;Sua Linguagem NÃO é especial&rdquo; expliquei porque tudo que você acha que é diferente e moderno na sua linguagem já existia décadas atrás.</p>
<p>MAS, isso dito, nesta nova série eu quero explorar um pouco o outro lado: ninguém disse que você não pode gostar da sua linguagem e, mais importante, dominá-la até o fim.</p>
<p>Veja o que eu fazia quando estava no começo da minha carreira e compare com o que você faz hoje em dia.</p>
<p>Correção: Colo Colo é do Chile, eu falei errado no vídeo. Sorry.</p>
<p>Links de referência:</p>
<ul>
<li>ASP WebForms (<a href="https://github.com/akitaonrails/ASP_Classic_WebForm"target="_blank" rel="noopener">https://github.com/akitaonrails/ASP_Classic_WebForm</a>)</li>
</ul>
<h2>Script<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Sua Linguagem É Especial! Parte 1</p>
<p>Se você assistiu meus 2 vídeos intitulados &ldquo;Sua Linguagem NÃO É Especial&rdquo; que conta como muitas das funcionalidades que você achava que só sua linguagem tinha e que eram coisas modernas na verdade vem de linguagens de 30, 40 ou 50 anos atrás.</p>
<p>Dependendo de como você assistir talvez alguns me interpretem sendo meio niilista. Pode parecer que eu estou dizendo pra não se importar com as linguagens que você gosta. Ou não gostar de nenhuma linguagem.</p>
<p>Na verdade sua linguagem favorita É especial mesmo ela NÃO sendo especial.</p>
<p>(&hellip;)</p>
<p>Se você ficou confuso, lembre de qual era meu ponto nos vídeos anteriores: você não é definido pela sua linguagem.</p>
<p>Muita gente coloca suas linguagens e ferramentas num pedestal e agem até defensivamente, querendo esconder, minimizar ou desconversar até as falhas óbvias da sua linguagem ou ferramenta. “Ah, mas a outra linguagem também é ruim em X ou Y”.</p>
<p>Eu sou promíscuo. Eu Não tenho afiliações, eu não tenho lealdade de marcas - eu não sou patrocinado, porque eu defenderia com unhas e dentes, de graça, uma marca que não é minha? -, eu não devo nada a nenhuma empresa. Elas é que devem pra mim e precisam me convencer a usar. Nada que você usa de graça hoje é de graça, lembre-se, se você Não está pagando, o produto é você. Esse é o conceito de freemium.</p>
<p>Além disso existe outra verdade fundamental sobre tecnologia: ela se torna obsoleta muito fácil. Tenho pena de quem era evangelista de Blackberry, já pensou? Ou os tempos difíceis de ser um evangelista de Perl hoje? Onde estão os evangelistas de Solaris? Deve ser sofrido tentar defender o Delphi hoje.</p>
<p>Eu não. Meu maior prazer é aprender, mas muito mais dominar, e dissecar todas as coisas que eu gosto. E jogar fora no minuto em que não me servem mais. Porque alguém acharia útil limitar o alcance do próprio conhecimento e experiência?</p>
<p>A melhor coisa que você pode fazer pra sua carreira é não tomar lado, nunca ter uma posição. No minuto que você faz isso, você se torna vulnerável. Mantenha sua opções abertas. Você sabe que está vulnerável quando alguém fala alguma coisa ruim da sua ferramenta favorita e você se sente ofendido. Se sentir ofendido é uma opção.</p>
<p>Existe uma observação empírica chamada Difusão de Inovação, que é o gráfico de adoção de alguma inovação.</p>
<p>Só vale a pena ser um evangelista se você está entre os Early Adopters e Early Majority. Você não quer ser um evangelista na fase de Late Majority ou Laggard. Defender Delphi hoje é ser um Laggard. Defender iOS em 2007 é Early Adopter. Em 2011 já é Late Majority. Quanto mais no fim dessa curva, menos seu conhecimento tem valor. Entenda que valor de conhecimento é inversamente proporcional à adoção.</p>
<p>Isso dito, deixa eu voltar ao ponto: sua linguagem é especial. Claro que é, como eu mostrei nos vídeos anteriores, é o resultado de uma longa linhagem de linguagens e pesquisas anteriores que culminaram de alguma forma no que você está usando hoje.</p>
<p>O erro é achar que ela vai continuar sendo o estado da arte pra sempre. Claro que não. Em breve seja lá qual linguagem você está usando vai se tornar o próximo Cobol ou Smalltalk.</p>
<p>Neste vídeo e no próximo quero contar duas histórias que aconteceram comigo quando eu tinha 24 anos, pra dar uma perspectiva pra vocês que estão nessa mesma faixa agora.</p>
<p>Se você está no começo de carreira, digamos que você usou Express, ou Django, ou Laravel, ou Rails. Você já não sentiu vontade de escrever seu próprio framework? Claro que sim, todo mundo já passou por isso.</p>
<p>Em 2001, dentre outras coisas, acho que já tinha uns 2 anos que eu usava ASP. Não o ASP.NET, o ASP clássico mesmo. Eu estava trabalhando numa ponto com, a Panamerican Sports Network, ou PSN. Ela fazia parte do grupo Hicks Muse Tate and Furst. Era dona de diversas equipes esportivas na América Latina como Cruzeiro, Corinthians, Figueirense, Colo colo da Colombia, Tigres e Chivas-Rajadas do Mexico, patrocinava o Alan Prost na Formula 1, tinha contratado o Pelé pra um programa de TV - eu lembro de ter ido na festa de lançamento - e no meu escritório em SP que tinha basicamente jornalistas, eles tinham lá colunistas como Sócrates e o Marcelo Fromer.
Eu falei, a bolha das ponto coms era absurda em torneira aberta de dinheiro.</p>
<p>Eu trabalhava na construção de todos esses sites com uma equipe que ficava na Flórida e alguns outros espalhados em outros países como Argentina, e acho que Venezuela. Nesse meio do caminho, a Microsoft estava se preparando pra atacar a dominância do Java. Eu estava muito empolgado com o lançamento do .NET na época. Mas ainda era meio beta. E naquela época as coisas não saíam tão rápido quanto saem hoje em dia.</p>
<p>O ASP que hoje chamamos de clássico, na época era o padrão de fato e de direito. Ele não iria embora por alguns anos ainda mesmo com o .NET. O .NET pra web tinha um framework novo, o ASP.NET Web Forms.</p>
<p>Comparado com o que se tinha no ASP clássico, PHP e o JSP da época, era uma evolução significativa. Em resumo, tinha 2 coisas que eu gostei, na época. A primeira era o próprio Web Forms. Em resumo, ele mantinha uma estrutura de dados que representava o estado da página e toda vez que você dava um POST ele mandava esse estado de volta pro servidor. O Estado ficava num campo hidden escondido no form encodado em Base64. Então a gente manipulava o estado nos callbacks como se fossem atributos num objeto que era serializado e desserializado entre o HTML e o C# e o Web Forms renderizava o HTML e conservava o estado escondido.</p>
<p>Isso era um Page Controller que já é um pattern ultrapassado, isso é pré adoção do modelo MVC que tínhamos no Java no framework Struts, lançado só alguns meses antes, e que era muito melhor. Mas pra quem era de ASP puro, o Page Controller já era um patttern bacana.</p>
<p>A segunda coisa era a idéia de manter sessões em banco de dados. O ASP puro mantinha sessões de usuários em memória. O problema é se você queria escalar horizontalmente. Agora se o load balancer mandasse o usuário pra um servidor diferente, ele perdia a sessão. Então você era obrigado a configurar o load balancer pra usar uma coisa chamada session affinity que fazia o mesmo usuário sempre cair no mesmo servidor pra manter a sessão. Isso era bem ruim na verdade.</p>
<p>No ASP.Net você podia escolher StateServer e com isso manter a sessão no banco de dados, daí podia escalar horizontalmente mais fácil. Isso foi antes de inventarmos o termo “arquitetura shared-nothing”.</p>
<p>O que eu resolvi fazer? Um framework em ASP puro que fazia essas duas coisas. Dá uma olhada. (puxa)</p>
<p>Eu basicamente criei classes pra cada elemento de um form pra representar no lado do servidor. Coloquei coisas que o ASP não tinha nativo como MD5 e Base64.</p>
<p>Eu tinha uma classe que representava uma Page (já que o pattern é o Page Controller onde cada página HTML tinha um controller no back end).</p>
<p>Eu fiz uma classe de Session que eu chamei de SessionPlus pra substituir o Session in memory do ASP por um que gravava em banco.</p>
<p>Eu fiz uma classe pra encapsular até o gerenciamento de upload de arquivos. Antes a gente lidava direto com o blob que vinha num form multipart.</p>
<p>Eu já tinha até o conceito de Validators, eu tinha classes ValidatorRequired que é como o validates presence de um ActiveRecord do Rails e até um Validator Regex com patterns pré-configurados pra regex comuns.</p>
<p>Cada classe cuidava da renderização HTML de cada elemento segundo a parametrização e aí no HTML em si eu precisava colocar o mínimo possível pra posicionar os elementos.</p>
<p>Eu até quis fazer um sistema de challenge-response. Em vez de postar a senha do usuário no form, eu carregava um gerador de MD5 no lado do Javascript. <br>
Daí o framework quando renderizava o HTML do Form também colocava um challenge num campo hidden do form, meio como hoje a gente coloca um meta tag pra evitar cross site request forgery.
Daí antes de submeter o form eu fazia um md5 do usuário, senha e esse challenge, daí no servidor era só fazer o mesmo md5 e comparar, daí eu não trafegava a senha. Ah sim, era md5 porque na época ainda não sabíamos que md5 tinha defeitos, só depois passamos a usar sha1.</p>
<p>Não era um esquema muito sofisticado nem muito seguro na verdade, mas em 2001 meu conhecimento de segurança só ia até aí. Eu tinha programado jeitos de evitar um pouco o cross site, e também duplo post ou post back.</p>
<p>Tudo foi escrito em VBScript! … é eu sei &hellip; que era uma versão menor do Visual Basic 6. Mas eu fui um pouco além. Em vez de deixar esse código junto com o código da aplicação, eu aprendi que era possível encapsular VBScript num XML em formato WSC que era Windows Script Components. Ele funciona como um componente COM binário e eu podia instalar no servidor de aplicação COM+ Components que vinha carregado em todo Windows Server e expor método públicos como CreateInstance e aí eu podia instanciar os objetos no servidor COM+ e usar de uma página ASP.</p>
<p>Normalmente você precisaria usar C++ ou Visual Basic 6 pra fazer componentes COM. Não sei porque, parecia que quase ninguém usava VBScript pra fazer componentes. A falta que fazia um site como stackoverflow …. Pensa que não existia o conceito de um NPM ou Nugget o máximo que tínhamos em open source era CPAN pra Perl, mas em Windows, nada. Ah, e não existia Git e nem Subversion, e vou te dizer que CVS era um parto pra usar.</p>
<p>Eu vou dizer que, pra ser 17 anos atrás, até que não tá tão ruim. Em em PHP ou Perl ainda não tinha muito o conceito de um “web framework” como temos hoje. No Java mesmo tava começando isso.</p>
<p>Agora, a parte ruim? Eu gastei um tempão fazendo os sites do jeito “normal” macarrônico em ASP e em paralelo eu ficava tentando fazer esse framework. Mas quando eu terminei ele, a bolha da internet tava crasheando e no final eu acabei não usando esse framework em nada muito útil.</p>
<p>Eu estava usando VBScript e ASP pra fazer coisas que eles não foram feitos pra fazer. A linguagem em si era bastante limitante. Mas o ponto foi pra dizer que eu fiz tudo que era possível fazer e também tentei dobrar a linguagem além do que deveria.</p>
<p>Foram acho que uns 3 anos programando todo tipo de intranet, extranet, website e tudo mais com ASP. Isso foi no ápice já do Late Majority do mundo ASP na curva de adoção.</p>
<p>ASP Clássico ainda iria durar muito tempo, eu reencontraria projetos em ASP em 2003, 2004 até 2008 e 2009. Mas esse framework pra mim serviu pra demonstrar as limitações do ASP e eu sabia que não havia mais muito o que fazer. O que poderia ser feito já havia sido feito. Era hora de abandonar o navio e foi o que eu fui gradativamente fazendo ao longo de 2002 e 2003.</p>
<p>Mesmo ainda haveria mercado pelos próximos 10 anos, mas já eram os Late Majority começando a virar Laggard, pelo simples fato que a Microsoft já deu sinais de abandonar o ASP pra perseguir o ASP.NET e como ASP não era open source, era o fim da linha.. O crash da internet meio que me forçou a mudar de rumo de qualquer jeito e depois disso eu caí no mundo SAP e Java de cabeça, que é tema pra outro episódio.</p>
<p>Porque eu contei essa história? Porque não importa qual linguagem ou plataforma eu pegasse, eu tentava sempre encontrar os limites. Não só seguir os tutoriais, ou os padrões, mas quebrar os padrões. É o que todo programador open source meio que tenta fazer hoje em dia também. O maior problema é que quando você investe 3 ou 4 anos da sua vida estudando e treinando com afinco, quando você conhece cada detalhe da tecnologia, a zona de conforto é muito sedutor. Você já é o senior, o melhor da tecnologia, poucos sabem tanto quanto você sabe.</p>
<p>Eu tive a sorte de recomeçar como junior em outra plataforma e fazer tudo de novo. Os primeiros 15 anos da minha carreira acho que de 1991 até 2006 foram consecutivos reinícios. Eu fui tecnicamente junior a maior parte da minha carreira, porque eu ficava reiniciando em novas plataformas o tempo todo, só depois de 2008 eu comecei a me aceitar como “senior”. E não foi de propósito, eu disse que a bolha da internet foi um momento interessante. O pico e o crash aconteceram de uma só vez, mas os anos que se antecederam pra chegar na bolha já eram acelerado. Não é muito diferente de como é hoje.</p>
<p>Eu não conhecia o conceito ainda de curva de adoção, mas intuitivamente eu tinha a noção. Mas não foi perda de tempo. Todos os conceitos que eu pratiquei em detalhes fazendo o framework deste episódio serviram pra me ajudar a avaliar melhor os frameworks que vieram depois. O ASP clásico e VBScript morreram, mas os conceitos de gerenciamento de sessão, validadores de form, gerenciadores de upload, separação de lógica server-side e client-side, componentização, serialização de objetos, encoders e o básico de criptografia, continuam sendo conhecimentos válidos. E esses conceitos é só o que tem nesse mini-framework, mas no próximo episódio você vai ver uma coisa um pouco maior que eu fiz alguns meses antes desta história, vocês vão gostar.</p>
<p>Meu código de 2001, comparado com os padrões de hoje em dia, é bem ruim. Bem ruim mesmo. Mas se alguém tiver curiosidade, eu subi esse mini-framework no meu Github, coloquei o link na descrição do vídeo. Não deixem de comentar abaixo, conta pra gente você também já fez seu próprio framework? Se curtiram mandem um joinha, assinem o canal e clique no sininho.</p>
]]></content:encoded><category>desenvolvimento-web</category><category>arquitetura-de-software</category><category>programacao</category><category>akitando</category></item><item><title>A Economia do Desenvolvimento de Software</title><link>https://akitaonrails.github.io/2017/06/22/a-economia-do-desenvolvimento-de-software/</link><guid isPermaLink="true">https://akitaonrails.github.io/2017/06/22/a-economia-do-desenvolvimento-de-software/</guid><pubDate>Thu, 22 Jun 2017 10:56:00 -0300</pubDate><description>&lt;p&gt;A comunidade de desenvolvimento de software está efervescente com tantas opções ao mesmo tempo. Temos dezenas de linguagens ativas e muito boas, como Go, &lt;a href="https://akitaonrails.github.io/elixir"&gt;Elixir&lt;/a&gt;, Clojure. Dezenas de frameworks excelentes, tanto no back-end quanto no front-end, incluindo React e Elm. Dezenas de metodologias sólidas, desde o bom e velho Agile até Continuous Deployment com microsserviços.&lt;/p&gt;
&lt;p&gt;Mas você é uma pequena startup de tecnologia ou uma equipe pequena dentro de uma grande corporação. Por onde começar?&lt;/p&gt;</description><content:encoded><![CDATA[<p>A comunidade de desenvolvimento de software está efervescente com tantas opções ao mesmo tempo. Temos dezenas de linguagens ativas e muito boas, como Go, <a href="/elixir">Elixir</a>, Clojure. Dezenas de frameworks excelentes, tanto no back-end quanto no front-end, incluindo React e Elm. Dezenas de metodologias sólidas, desde o bom e velho Agile até Continuous Deployment com microsserviços.</p>
<p>Mas você é uma pequena startup de tecnologia ou uma equipe pequena dentro de uma grande corporação. Por onde começar?</p>
<p>A recomendação é simples: faça a menor coisa que funciona primeiro. Sempre. Claro, evite o &ldquo;rápido e sujo&rdquo; ao máximo. Mas não seja paranoico nem exagere na engenharia.</p>
<p>Super-engenharia é tão cara quanto fazer as coisas de forma &ldquo;suja&rdquo;.</p>
<h3>Adicione &ldquo;Tempo&rdquo; à sua equação<span class="hx:absolute hx:-mt-20" id="adicione-tempo-à-sua-equação"></span>
    <a href="#adicione-tempo-%c3%a0-sua-equa%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O erro mais comum que a maioria das pessoas comete é ignorar a variável &ldquo;Tempo&rdquo;.</p>
<p>Todo mundo vai a conferências de tecnologia, lê posts badalados ou assiste screencasts chamativos. A conclusão errada que tiram é: <em>&ldquo;a Netflix usa isso, então deve ser bom pra mim, porque eu quero me tornar a Netflix&rdquo;</em>.</p>
<p>O pressuposto falso é que a Netflix, ou o Google, ou o Facebook, ou o Spotify, é um sistema estático e sempre funcionou da forma como divulga hoje.</p>
<p>As pessoas esquecem que toda empresa unicórnio teve um <strong>dia 1</strong>. E no dia 1, elas não usavam microsserviços. Não tinham React.js. Não tinham Go nem Elixir. Algumas nem tinham bons programadores de início. A Netflix começou como um serviço de aluguel de VHS, lembra? O Google começou num quarto de dormitório universitário com componentes de hardware montados num <a href="http://www.complex.com/pop-culture/2013/02/50-things-you-didnt-know-about-google/lego-server-rack"target="_blank" rel="noopener">rack de Lego</a>.</p>
<p>Imagine que você é fã de uma celebridade. Essa celebridade tem uma Lamborghini. Você quer ser como essa celebridade um dia. O que você faz? Compra a mesma Lamborghini que ela tem?</p>
<p>Se você fizer isso, a única coisa que vai conseguir é uma DÍVIDA ENORME para pagar.</p>
<p>Pare de invejar a Lamborghini do rico.</p>
<h3>Dívida (Técnica)<span class="hx:absolute hx:-mt-20" id="dívida-técnica"></span>
    <a href="#d%c3%advida-t%c3%a9cnica" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Esse não é um termo novo nem um tema novo. Cada decisão que você toma em programação é um <strong>compromisso</strong> entre o que o &ldquo;futuro ideal&rdquo; deveria ser e o que você consegue fazer agora. Tomar empréstimo para comprar a Lamborghini equivalente cedo demais, e você vai ter que lidar com uma Dívida (Técnica) impagável. Você vai travar, vai parar e não vai fazer nada além de pagar essa dívida daqui pra frente.</p>
<p>Você quer &ldquo;codar mais rápido&rdquo;, então pula a escrita de testes automatizados, porque acha que vai te atrasar. E você está certo. O desenvolvimento orientado a testes não te deixa mais rápido de início, e nem deveria. Ele protege seu eu futuro do seu eu presente e evita que você acumule Dívida Técnica.</p>
<p>De novo: você esqueceu a variável &ldquo;Tempo&rdquo;.</p>
<p>Você escreve e entrega rápido no primeiro mês. Depois que a primeira versão está em produção, você começa a ser obrigado a pagar a dívida. Sua produtividade cai. Bugs de regressão aparecem. Cada nova funcionalidade que você tenta adicionar quebra algo de forma inesperada. Como não tem testes automatizados, você fica corrigindo as mesmas coisas várias vezes. A dívida vai cobrar, e você vai pagar. De um jeito ou de outro.</p>
<p>Suítes mínimas de testes são como um <strong>Seguro</strong>. Você não precisa dele agora, no dia 1. Mas no dia 100 você vai ficar feliz que tem.</p>
<p>Fazer microsserviços demais no dia 1 é dívida. Parece ótimo estar fazendo aquela coisa bacana que você leu num blog. No dia 100, com sua equipe de 3 pessoas e uma dúzia de microsserviços, você não vai fazer nada além de pagar a dívida acumulada, com <strong>Juros</strong>. Cada novo deploy de microsserviço quebra seu sistema. E claro, você não adicionou monitoramento, não adicionou testes integrados. Então toda vez que você sobe algo, outra coisa quebra de forma inesperada.</p>
<p>Ainda está curtindo a Lamborghini antecipada?</p>
<h3>O Mítico Homem-Mês<span class="hx:absolute hx:-mt-20" id="o-mítico-homem-mês"></span>
    <a href="#o-m%c3%adtico-homem-m%c3%aas" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Philip Calçado escreve ótimos posts e apresentações sobre microsserviços do jeito certo. Se você leva esse assunto a sério, recomendo fortemente que leia seus textos, como o <a href="http://philcalcado.com/2017/06/11/calcados_microservices_prerequisites.html"target="_blank" rel="noopener">&ldquo;Prerequisites&rdquo;</a> ou sua apresentação <a href="https://www.infoq.com/news/2017/05/economics-microservices"target="_blank" rel="noopener">&ldquo;Economics&rdquo;</a>.</p>
<p>Ele cita corretamente <strong>O Mítico Homem-Mês</strong>, de Fred Brooks. Insisto que os desenvolvedores leiam esse livro pequeno. É impressionante como a indústria inteira ainda repete exatamente os mesmos erros que Brooks relatou em seu livro, de projetos dos anos 60!</p>
<p>Para mim, microsserviços são um subproduto de empresas de tecnologia com programadores em excesso. Quando você ultrapassa a barreira de 5 desenvolvedores, um sistema &ldquo;monolítico&rdquo; com organização ruim e poucos testes automatizados pode ficar difícil de lidar. A consequência natural é a vontade de fragmentar. Você cria 2 equipes e 2 microsserviços e tenta coordenar. As equipes começam a se isolar e a praticar o jogo de culpar o outro quando novos bugs aparecem (<em>&ldquo;é culpa do microsserviço da outra equipe&rdquo;</em>).</p>
<p>É a <a href="http://www.melconway.com/Home/Conways_Law.html"target="_blank" rel="noopener">Lei de Conway</a> aplicada da forma errada:</p>
<blockquote>
  <p>Qualquer organização que projeta um sistema (definido de forma ampla) produzirá um design cuja estrutura é uma cópia da estrutura de comunicação da organização.</p>

</blockquote>
<p>Também entra em jogo outro capítulo do livro do Brooks: o <a href="http://wiki.c2.com/?SecondSystemEffect"target="_blank" rel="noopener">Efeito do Segundo Sistema</a>. Todo desenvolvedor e toda startup precisa assumir que a primeira versão de qualquer coisa não é o melhor design. E o segundo sistema (a primeira grande reescrita) é <strong>o sistema mais perigoso</strong> que você vai construir. Como Brooks descreve:</p>
<blockquote>
  <p>O primeiro trabalho de um arquiteto tende a ser enxuto e limpo. Ele sabe que não sabe o que está fazendo, então procede com cuidado e grande contenção.</p>

</blockquote>
<blockquote>
  <p>Enquanto projeta o primeiro trabalho, adornos e embelezamentos vão ocorrendo a ele. Esses ficam guardados para serem usados &ldquo;na próxima vez.&rdquo; Cedo ou tarde o primeiro sistema está pronto, e o arquiteto, com confiança firme e maestria demonstrada nessa classe de sistemas, está pronto para construir o segundo.</p>

</blockquote>
<blockquote>
  <p>Esse segundo é o sistema mais perigoso que um homem já projeta. Quando faz o terceiro e os posteriores, suas experiências anteriores se confirmarão mutuamente quanto às características gerais de tais sistemas, e as diferenças identificarão as partes de sua experiência que são particulares e não generalizáveis.</p>

</blockquote>
<blockquote>
  <p>A tendência geral é super-projetar o segundo sistema, usando todas as ideias e adornos que foram cautelosamente deixados de lado no primeiro. O resultado, como diz Ovídio, é uma &ldquo;grande pilha.&rdquo;</p>

</blockquote>
<p>O que acontece depois? O CEO, o conselho, os investidores, seja lá quem for, começam a fazer a coisa errada: contratar mais e mais pessoas. Tomar mais e mais empréstimos. Em vez de pagar as dívidas, acumulam mais dívida. Caíram na armadilha que Brooks relatou no primeiro capítulo e que dá nome ao livro: O Mítico Homem-Mês.</p>
<blockquote>
  <p>Adicionar mão de obra a um projeto de software atrasado o atrasa ainda mais.</p>

</blockquote>
<blockquote>
  <p>Projetos de programação complexos não podem ser perfeitamente particionados em tarefas discretas que possam ser executadas sem comunicação entre os trabalhadores e sem estabelecer um conjunto de inter-relações complexas entre as tarefas e os trabalhadores que as executam.</p>

</blockquote>
<blockquote>
  <p>Portanto, alocar mais programadores a um projeto que está atrasado o atrasará ainda mais. Isso porque o tempo necessário para os novos programadores aprenderem sobre o projeto e a sobrecarga de comunicação aumentada consumirão uma quantidade cada vez maior do tempo disponível no calendário.</p>

</blockquote>
<p>De novo: ignorando a variável &ldquo;Tempo&rdquo;. E confundindo &ldquo;dívida&rdquo; com &ldquo;investimento&rdquo;.</p>
<p>Antes de tudo, faça um favor a si mesmo e <a href="http://amzn.to/2sFbkWq"target="_blank" rel="noopener">leia o maldito livro</a>, duas vezes.</p>
<h3>Faça Agile do jeito certo! E não, não é Kanban!<span class="hx:absolute hx:-mt-20" id="faça-agile-do-jeito-certo-e-não-não-é-kanban"></span>
    <a href="#fa%c3%a7a-agile-do-jeito-certo-e-n%c3%a3o-n%c3%a3o-%c3%a9-kanban" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Se há uma coisa boa que todo o barulho do Lean produziu, é a noção de &ldquo;Produto Mínimo Viável&rdquo; ou MVP. Alguns chamam de &ldquo;protótipo&rdquo;. Outros chamam de &ldquo;lançar em Beta&rdquo; ou simplesmente &ldquo;versão 1.0&rdquo;. Não importa o nome. É o reconhecimento de que você não sabe muita coisa no início, então fazer engenharia excessiva na primeira versão é desperdício de tempo. Lean é sobre controlar o &ldquo;desperdício&rdquo;: fazemos o mínimo que funciona, medimos os resultados e evoluímos a partir daí.</p>
<p>Os cínicos vão dizer que qualquer protótipo que chega a produção nunca morre. E não estão errados.</p>
<p>O equilíbrio está em não fazer a versão &ldquo;rápida e suja&rdquo;. É pra isso que existem as técnicas ágeis. Faça o mínimo, organize o mínimo. É pra isso que existem os padrões de design orientado a objetos, do GoF ao DDD. Você não precisa aplicar TODOS os padrões, e é justamente isso que significa &ldquo;super-engenharia&rdquo;. Mas dá pra fazer o monolito mínimo que vai te permitir evoluir depois.</p>
<p>As pessoas criticam Ruby on Rails por não ser organizado &ldquo;o suficiente&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>, o criador do <a href="https://github.com/trailblazer/trailblazer"target="_blank" rel="noopener">Trailblazer</a>, tem um ponto válido.</p>
<p>Rails feito errado é ruim. Mas a conclusão NÃO deveria ser: <em>&ldquo;vamos fazer microsserviços&rdquo;</em>. Esse salto de fé é idiota e não faz sentido por todos os motivos que citei acima.</p>
<p><em>&ldquo;Vamos fazer Agile de verdade e orientação a objetos de verdade&rdquo;</em> deveria ser a resposta inicial.</p>
<p>E por &ldquo;Agile&rdquo;, esqueça os post-its idiotas, esqueça as equações de estimativa baseadas em numerologia (isso dá um post inteiro por si só, porque story points e velocity são úteis, mas adicionar Montecarlo e outras coisas baseadas em distribuição Gaussiana não faz sentido). As únicas coisas &ldquo;Ágeis&rdquo; com as quais você deveria se preocupar são as <a href="http://www.extremeprogramming.org/"target="_blank" rel="noopener">técnicas de Extreme Programming (XP)</a>, <strong>incluindo</strong> os timeboxes baseados em iterações.</p>
<p>Você <strong>PRECISA</strong> fazer timeboxes. Pare, reavalie, mude de direção, e então continue. O modelo de &ldquo;Pull&rdquo; só faz sentido quando sua direção é muito, muito clara, escrita em pedra e imutável, como numa linha de produção de fábrica. (Que é exatamente onde o conceito de Pull, e o Lean, nasceram!)</p>
<p>Iterações, como testes automatizados, são como um Seguro. Você nunca consegue evitar todo o desperdício, mas pode minimizá-lo. Você pode se dar ao luxo de jogar fora o trabalho de uma Iteração inteira. Depois da iteração você mede os resultados e descarta se necessário, mudando de direção no processo. Jogar fora o que não serve é tão importante quanto adicionar coisas novas. Se você só acumula, está com um <a href="https://en.wikipedia.org/wiki/Compulsive_hoarding"target="_blank" rel="noopener">transtorno de acumulação</a>!</p>
<p>Na dúvida, faça XP. Sim, parece mais &ldquo;difícil&rdquo;, e o Kanban é &ldquo;mais simples&rdquo; de explicar. Mas isso é motivo suficiente?</p>
<h3>Esqueça Performance Bruta quando você NÃO precisa dela!<span class="hx:absolute hx:-mt-20" id="esqueça-performance-bruta-quando-você-não-precisa-dela"></span>
    <a href="#esque%c3%a7a-performance-bruta-quando-voc%c3%aa-n%c3%a3o-precisa-dela" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Outro problema é escolher linguagens ou frameworks por causa de performance.</p>
<p>Se você está no negócio de desenvolvimento web, isso é um DESPERDÍCIO enorme.</p>
<p>Entenda essa verdade: seus servidores vão ficar OCIOSOS na maior parte do tempo. E se você acha que seu web app está lento, a lentidão vem da PROGRAMAÇÃO RUIM que você fez, não da linguagem. Nenhuma linguagem boa vai salvar um programador ruim. Sempre digo que se performance fosse assim tão importante, todo mundo deveria estar usando C.</p>
<p>A maior parte do que você serve em apps baseados em HTTP, seja conteúdo para o usuário, seja resultado de API GET, pode ser <strong>CACHEADA</strong>! Se você não está usando uma <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>, está fazendo errado.</p>
<p>Sim, sim, sim, você acha que está construindo o próximo Spotify. Não está, pelo menos 99% de vocês não estão. E os 1% que fazem engenharia personalizada, com técnicas e stack próprios, e com sucesso: vocês são os 1%. Na verdade, são uma fração dos 1%. Não assuma que o que você faz é bom para o resto da população.</p>
<p>90% do que a maioria das pequenas empresas e desenvolvedores web solo precisam é só uma conta no Shopify e uma instalação padrão do WordPress. É isso.</p>
<blockquote>
  <p>Você está fazendo desenvolvimento de infraestrutura, como construir componentes customizados para Docker, Kubernetes, Terraform? Ferramentas de linha de comando? Daemons? Então escolha GO.</p>

</blockquote>
<blockquote>
  <p>Está fazendo bibliotecas embutidas? Talvez a próxima geração do OpenSSL? Drivers? Então escolha RUST.</p>

</blockquote>
<blockquote>
  <p>Está fazendo desenvolvimento mobile? Não tem muita escolha: Swift para iOS, Kotlin/Java para Android. Ou React Native para apps mais simples.</p>

</blockquote>
<blockquote>
  <p>Está realmente construindo o próximo Whatsapp? O próximo Waze? O próximo Snap? Tem centenas ou milhares de usuários precisando de conexões de longa duração em redes não confiáveis fazendo broadcast de mensagens? Ou está construindo a próxima evolução de bancos de dados NoSQL distribuídos? Ou qualquer coisa que tenha o sentido real de &ldquo;Distribuído&rdquo; em sua definição? Então escolha ELIXIR.</p>

</blockquote>
<blockquote>
  <p>Está fazendo uma aplicação web baseada em CRUD? Vai de RAILS e não olha pra trás.</p>

</blockquote>
<blockquote>
  <p>Está fazendo tudo isso ao mesmo tempo? Use todas as alternativas então. E espero que tenha orçamento, porque vai precisar de uma equipe grande.</p>

</blockquote>
<h3>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A Economia só faz sentido quando você considera o dia 1. Não esqueça a variável &ldquo;Tempo&rdquo;. Ela faz toda a diferença.</p>
<p>Você sempre vai ter Dívidas a pagar. Então seja inteligente na escolha de qual tipo de dívida você assume. Porque vai ter que pagar de volta eventualmente.</p>
<p>De novo: leia Brooks. Ele disse isso décadas atrás: <a href="http://worrydream.com/refs/Brooks-NoSilverBullet.pdf"target="_blank" rel="noopener">NÃO EXISTE BALA DE PRATA</a>. Não se iluda. Uma nova linguagem, um novo framework, uma nova arquitetura. Nada disso vai te salvar.</p>
<p>Em qualquer empreitada tecnológica, código não é a única preocupação. Diria até que numa startup de tecnologia, código é apenas 20% do problema. Uma startup de tecnologia é só uma empresa, como qualquer outra. Se você é o fundador ou CEO, tem que lidar com os 80% restantes: marketing, contabilidade, recursos humanos, questões legais, etc. Já é difícil o suficiente sem que sua equipe técnica te traga uma Dívida Técnica indesejada, a Lamborghini desnecessária, que você não vai conseguir pagar.</p>
<p>Você pode começar humilde no dia 1. Continue evoluindo, continuamente. Esse é o núcleo de qualquer processo Agile ou Lean: <a href="https://www.graphicproducts.com/articles/what-is-kaizen/"target="_blank" rel="noopener">KAIZEN</a>. Escolha dívidas inteligentes, pague-as aos poucos, continuamente. É o mesmo raciocínio de fazer um empréstimo no banco.</p>
<blockquote>
  <p>O dia 7.200 da Netflix NÃO é o seu dia 1.</p>

</blockquote>
<blockquote>
  <p>O dia 4.800 do Facebook NÃO é o seu dia 1.</p>

</blockquote>
<blockquote>
  <p>O dia 6.809 do Google NÃO é o seu dia 1.</p>

</blockquote>
<blockquote>
  <p>Seja humilde. Entregue rápido. Pague suas Dívidas. Continue evoluindo continuamente. Pare de acreditar em contos de fadas e balas de prata.</p>

</blockquote>
]]></content:encoded><category>engenharia-de-software</category><category>agile</category><category>arquitetura-de-software</category><category>off-topic</category></item><item><title>Compartilhando models entre apps Rails - Parte 2</title><link>https://akitaonrails.github.io/2016/10/03/compartilhando-models-entre-apps-rails-parte-2/</link><guid isPermaLink="true">https://akitaonrails.github.io/2016/10/03/compartilhando-models-entre-apps-rails-parte-2/</guid><pubDate>Mon, 03 Oct 2016 15:57:00 -0300</pubDate><description>&lt;p&gt;Vamos continuar de onde parei na &lt;a href="http://www.akitaonrails.com/2016/10/03/compartilhando-models-entre-aplicacoes-rails-parte-1"target="_blank" rel="noopener"&gt;Parte 1&lt;/a&gt;, onde descrevi rapidamente como extrair lógica reusável de models de uma app Rails para um Rubygem testável.&lt;/p&gt;
&lt;p&gt;Se eu fosse construir uma segunda app Rails conectando direto ao mesmo banco da primeira, bastaria adicionar a dependência do gem extraído:&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;Aí recriar os models incluindo os mesmos Concerns, e remover a capacidade de rodar &lt;code&gt;bin/rails db:migrate&lt;/code&gt; na app secundária (criando tasks vazias com o mesmo nome, por exemplo).&lt;/p&gt;</description><content:encoded><![CDATA[<p>Vamos continuar de onde parei na <a href="http://www.akitaonrails.com/2016/10/03/compartilhando-models-entre-aplicacoes-rails-parte-1"target="_blank" rel="noopener">Parte 1</a>, onde descrevi rapidamente como extrair lógica reusável de models de uma app Rails para um Rubygem testável.</p>
<p>Se eu fosse construir uma segunda app Rails conectando direto ao mesmo banco da primeira, bastaria adicionar a dependência do gem extraí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">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>Aí recriar os models incluindo os mesmos Concerns, e remover a capacidade de rodar <code>bin/rails db:migrate</code> na app secundária (criando tasks vazias com o mesmo nome, por exemplo).</p>
<p>Aliás, esse é um grande detalhe que deixei de fora na Parte 1: até aqui, o schema estava <strong>congelado</strong> no <a href="https://github.com/Codeminer42/cm42-central-support/blob/master/spec/support/rails_app/db/schema.rb"target="_blank" rel="noopener">gem central-support</a>.</p>
<p>Daqui pra frente, você precisa controlar a evolução das tabelas mapeadas no gem a partir do próprio gem. A melhor abordagem é usar o <code>spec/support/rails_app</code> e criar normalmente migrations novas com <code>bin/rails g migration</code> por lá. Depois você move a migration para a pasta <code>lib/generators/central/templates/migrations</code>.</p>
<p>O <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> cuida de disponibilizar uma task <code>central:install</code> que joga as migrations novas na pasta <code>db/migrate</code> da sua aplicação como de costume. Basta rodar <code>bundle update central-support</code> para pegar as últimas mudanças, rodar <code>bin/rails g central:install</code> para criar as migrations novas (ele pula automaticamente as existentes) e rodar o <code>bin/rails db:migrate</code> normal. O código de um gerador de migration é bem simples, dá pra fazer assim:</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>O <code>migration_template</code> cuida de adicionar o timestamp correto no arquivo de migration, então você não precisa colocar manualmente, e o nome do arquivo template pode ser algo simples como <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>Dito isso, tem um segundo desafio que adicionei na minha app secundária interna: eu queria que ela tivesse o seu próprio banco principal e usasse o banco do Central como fonte secundária somente leitura.</p>
<p>Então as migrations da app secundária (vamos chamar de Central-2) vão rodar contra o seu próprio banco principal, e não contra o banco principal do Central. Isso traz o seguinte problema: a suíte de testes precisa conseguir criar e migrar os dois bancos de teste, isolados do Central. Só em produção é que o Central-2 deve conectar no banco do Central.</p>
<p>Toda aplicação Rails tem um <code>config/database.yml.sample</code> e um <code>db/schema.rb</code>, então comecei criando um <code>config/database_central.yml.sample</code> e um <code>db_central/schema.rb</code>.</p>
<p>O <code>config/database_central.yml.sample</code> já fica interessante:</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>Em produção, ele vai usar a variável de ambiente <code>DATABASE_CENTRAL_URL</code> para conectar no banco principal do Central.</p>
<p>Rodando os testes localmente, ele simplesmente conecta num banco local chamado <code>central_test</code>.</p>
<p>Agora, ao rodar testes no Gitlab-CI (ou qualquer outro CI), eu preciso configurar o <code>DATABASE_CENTRAL_URL</code> para apontar para um banco Postgresql secundário de testes.</p>
<p>No Gitlab, eu configuro o script de build assim:</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>Repare como eu copio os arquivos &ldquo;.sample&rdquo; para garantir que existam. E como eu rodo tasks que você conhece como <code>db:create db:schema:load</code> para criar o banco de teste normal, mas também tasks que você não conhece como <code>central:db:create central:db:schema:load</code>.</p>
<p>Eu defini essas tasks em <code>lib/tasks/db_central.rake</code> assim:</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>É assim que defino um namespace de tasks Rake que começam com <code>central:</code>, e cada uma delas conecta no banco secundário descrito no <code>database_central.yml</code>. A sintaxe estranha vem do meu <a href="https://github.com/akitaonrails/chainable_methods"target="_blank" rel="noopener"><code>chainable_methods</code></a>, não dê muita atenção.</p>
<p>O <code>db_central/schema.rb</code> é basicamente uma cópia do <code>spec/support/rails_app/db/schema.rb</code> do gem central-support, com as mesmas tabelas. O runner de specs tanto do gem quanto dessa app secundária só carrega o schema no banco de teste.</p>
<p>Agora que temos os fundamentos básicos para os specs no lugar, podemos focar em como a aplicação em si consome esses models externos.</p>
<p>Começamos adicionando um initializer como <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>Nesse caso eu leio do arquivo sample porque, diferente do build de CI, quando eu faço deploy no Heroku eu não tenho um script para copiar o sample para o yaml final. Isso popula a constante <code>DB_CENTRAL</code> com a URL do banco armazenada na variável de ambiente <code>DATABASE_CENTRAL_URL</code> que eu preciso configurar.</p>
<p>Aí crio um arquivo novo chamado <code>app/models/remote_application_record.rb</code> mais ou menos assim:</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>É assim que você cria um novo connection pool para a configuração do banco secundário. Você deve ter esse <code>establish_connection</code> em um único lugar e fazer os models herdarem daqui. O <code>abstract_class = true</code> faz com que o ActiveRecord deixe de tentar carregar de uma tabela com o mesmo nome dessa classe.</p>
<p>Em seguida temos um <code>default_scope</code> travando o model como <code>readonly</code>. A gente quer abrir mão disso no ambiente de teste, porque eu ainda quero deixar o Factory Girl popular o banco de teste com dados falsos para os specs. Mas é uma boa ideia ter isso em produção só para garantir.</p>
<p>Por fim, posso criar todos os models que preciso, como <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>A partir daqui posso fazer queries Arel normais como <code>Central::Team.not_archived.limit(5)</code>.</p>
<h3>Conclusões<span class="hx:absolute hx:-mt-20" id="conclusões"></span>
    <a href="#conclus%c3%b5es" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Se ainda não viu, dá uma olhada na <a href="http://www.akitaonrails.com/2016/10/03/compartilhando-models-entre-aplicacoes-rails-parte-1"target="_blank" rel="noopener">Parte 1</a> para mais detalhes.</p>
<p>Essa é uma receita simples para compartilhar lógica de model entre uma app Rails principal de leitura e escrita e uma app Rails secundária de só leitura. Elas compartilham a maioria (não todos) dos mesmos models, compartilham a mesma lógica (através de alguns Concerns) e compartilham o mesmo banco.</p>
<p>Nesse caso particular, a abordagem recomendada é criar um <a href="https://devcenter.heroku.com/articles/heroku-postgres-follower-databases"target="_blank" rel="noopener">Follower database</a>, que é como o Heroku chama um banco secundário replicado, e fazer minha aplicação secundária conectar nele (já que ela só precisa de uma fonte somente leitura).</p>
<p>Para cenários mais complicados, você vai precisar de uma solução mais elaborada como uma camada de API HTTP para garantir que apenas uma App gerencie as migrations dos models. Mas a abordagem do Rubygem deve ser &ldquo;boa o suficiente&rdquo; para muitos casos.</p>
<p>Se eu realmente precisar ir por esse caminho, não vai ser difícil transformar esse pequeno gem numa app Rails API completa. Se você nem consegue separar a lógica em Concern, também não vai conseguir separar como APIs, então encare isso como um exercício rápido, um primeiro passo para criar uma <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>E como bônus, considere contribuir com os projetos open source <a href="https://github.com/Codeminer42/cm42-central"target="_blank" rel="noopener">Central</a> e <a href="https://github.com/Codeminer42/cm42-central-support"target="_blank" rel="noopener">central-support</a>. Pretendo construir uma alternativa competitiva ao Pivotal Tracker/Trello e estamos chegando lá!</p>
]]></content:encoded><category>rails</category><category>bancos-de-dados</category><category>arquitetura-de-software</category></item><item><title>Compartilhando models entre aplicações Rails - Parte 1</title><link>https://akitaonrails.github.io/2016/10/03/compartilhando-models-entre-aplicacoes-rails-parte-1/</link><guid isPermaLink="true">https://akitaonrails.github.io/2016/10/03/compartilhando-models-entre-aplicacoes-rails-parte-1/</guid><pubDate>Mon, 03 Oct 2016 15:00:00 -0300</pubDate><description>&lt;p&gt;&lt;em&gt;&amp;ldquo;Cria um microservice e expõe uma API.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Essa seria a resposta rápida se você perguntar para qualquer desenvolvedor como compartilhar lógica de negócio entre aplicações.&lt;/p&gt;
&lt;p&gt;Apesar de fazer sentido em muitas situações, essa resposta deixa a desejar em vários outros casos.&lt;/p&gt;
&lt;p&gt;Meu TL;DR para algumas situações é o seguinte: você pode organizar a lógica dos seus models como &lt;a href="http://api.rubyonrails.org/classes/ActiveSupport/Concern.html"target="_blank" rel="noopener"&gt;ActiveSupport::Concerns&lt;/a&gt; (ou Modules Ruby puros, se preferir) e movê-los para uma Rubygem que suas aplicações podem consumir.&lt;/p&gt;</description><content:encoded><![CDATA[<p><em>&ldquo;Cria um microservice e expõe uma API.&rdquo;</em></p>
<p>Essa seria a resposta rápida se você perguntar para qualquer desenvolvedor como compartilhar lógica de negócio entre aplicações.</p>
<p>Apesar de fazer sentido em muitas situações, essa resposta deixa a desejar em vários outros casos.</p>
<p>Meu TL;DR para algumas situações é o seguinte: você pode organizar a lógica dos seus models como <a href="http://api.rubyonrails.org/classes/ActiveSupport/Concern.html"target="_blank" rel="noopener">ActiveSupport::Concerns</a> (ou Modules Ruby puros, se preferir) e movê-los para uma Rubygem que suas aplicações podem consumir.</p>
<p>Repare que estou falando apenas de Models, não de Controllers ou Views. Para compartilhar essas outras coisas você precisaria de uma Rails Engine completa. Mas em muitos casos que vi a vontade era apenas compartilhar a lógica de negócio entre aplicações, mantendo o front-end separado.</p>
<p>Um pequeno exemplo desse cenário é o projeto open source em que venho trabalhando nas últimas semanas. <a href="https://github.com/Codeminer42/cm42-central"target="_blank" rel="noopener">Central</a>, uma alternativa ao Pivotal Tracker/Trello - caso você se interesse.</p>
<p>Há alguns dias comecei um novo projeto (apenas para uso interno) que precisava consultar os mesmos models do Central. Eu não queria implementar APIs HTTP nesse momento, e a nova aplicação teria seus próprios models com relacionamentos com os models do Central (tratando-os como read-only).</p>
<p>Depois de alguns refactorings, a maioria dos models do Central ficou parecida com este:</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>E tenho essa dependência no <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>Sempre que mudo as concerns, rodo um <code>bundle update central-support</code> nos projetos (esse é o cuidado que você precisa ter para evitar lidar com models desatualizados).</p>
<p>Isso foi possível porque a maioria desses models já estava madura e estável e eu não vou mexer neles com frequência. Eu não recomendo expor dependências instáveis (como gems ou APIs, tanto faz), porque essa é a receita para uma dor de cabeça enorme com breaking changes em cascata por causa de dependências desatualizadas que mudam com frequência demais.</p>
<blockquote>
  <p>Você deve APENAS expor lógica de negócio que esteja razoavelmente estável (mudanças apenas de semana em semana, mais ou menos).</p>

</blockquote>
<p>A jornada toda foi montar uma certa estrutura de Rubygems, organizar os models originais em Concerns (o que não quebra nenhum comportamento), garantir que os specs continuassem passando, e então mover o conteúdo (models e specs) para a nova Rubygem e garantir que os specs passassem por lá.</p>
<p>Foi assim que construí uma dependência open source secundária do Central, chamada <a href="https://github.com/Codeminer42/cm42-central-support"target="_blank" rel="noopener">Central Support</a>. Como muitas gems, o arquivo principal <a href="https://github.com/Codeminer42/cm42-central-support/blob/master/lib/central/support.rb"target="_blank" rel="noopener">lib/central/support.rb</a> é só um monte de &lsquo;requires&rsquo; para carregar todas as dependências.</p>
<p>Então organizei metodicamente a lógica em concerns, como <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>, que é só a extração das associações de Active Record do model &lsquo;Team&rsquo;.</p>
<p>Recortar do Central, colar no Support. Quando toda a lógica relevante já tinha sido movida, consegui mover o spec inteiro do model Team, basicamente sem alterações, e fazê-lo rodar. Toda vez que eu movia um pedacinho, dava <code>bundle update</code> na gem e rodava a suíte principal de specs para garantir que nada tinha quebrado.</p>
<p>E essa é a parte difícil: montar um sandbox onde essas concerns possam rodar e ser testadas.</p>
<p>Para começar, precisei construir uma aplicação Rails mínima dentro da pasta de specs, em <code>spec/support/rails_app</code>. E ali eu pude colocar models falsos que incluem as concerns que acabei de extrair do Central.</p>
<p>Existe pouca documentação sobre como fazer isso, mas acho que dá para fazer um <code>rails new</code> e começar dali, ou copiar a minha pasta <code>rails_app</code> para ter o mínimo necessário. Meu caso é mais simples porque essa gem não é de propósito geral, então não preciso rodá-la contra versões diferentes do Rails, por exemplo.</p>
<p>Essa app de teste interna precisa ter um <a href="https://github.com/Codeminer42/cm42-central-support/blob/master/spec/support/rails_app/Gemfile"target="_blank" rel="noopener"><code>Gemfile</code></a> cuidadosamente montado:</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>Você não precisa adicionar as gems do <a href="https://github.com/Codeminer42/cm42-central-support/blob/master/central-support.gemspec"target="_blank" rel="noopener">gemspec</a> principal. Mas você pode remover as dependências de desenvolvimento que iriam para o gemspec e mantê-las no Gemfile da app de teste.</p>
<p>Agora, a partir do <a href="https://github.com/Codeminer42/cm42-central-support/blob/master/Gemfile"target="_blank" rel="noopener"><code>Gemfile</code> principal</a> você pode fazer:</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>A maioria dos tutoriais de como construir uma Rubygem coloca uma linha para carregar dependências do gemspec, mas aqui estamos substituindo isso pelo Gemfile da app de teste. Esse é o manifest que será carregado quando rodarmos <code>bundle exec rspec</code>, por exemplo.</p>
<p>Por falar nisso, esse é o <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>Para visualizar como tudo se encaixa:</p>
<ul>
<li><code>bundle exec rspec</code> carrega o <code>Gemfile</code> principal</li>
<li>o <code>Gemfile</code> principal carrega a partir do <code>Gemfile</code> da app de teste interna</li>
<li>esse <code>Gemfile</code> da app de teste interna requer o gemspec de <code>../../../..</code> e os grupos de gems de development e test (incluindo Rspec, Factory Girl, etc)</li>
<li>o gemspec requer as dependências de runtime como &ldquo;activesupport&rdquo;, &ldquo;enumerize&rdquo;, etc</li>
<li>finalmente, o <code>rails_helper.rb</code> listado acima é carregado.</li>
</ul>
<p>Ali na linha 11, o runner executa um comando para fazer <code>cd</code> na pasta raiz da app de teste interna e rodar <code>db:schema:load</code>, então você precisa de um <code>db/schema.rb</code> pronto para ser carregado, assim como um <code>config/database.yml</code>.</p>
<p>O <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> é mais padrão, com configurações opcionais para test coverage, etc.</p>
<p>Os models dentro da app de teste interna são as partes importantes, porque são o meio para incluir as concerns extraídas em um formato executável. O <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> é um exemplo:</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>E com isso, consegui mover os specs sem modificações diretamente do projeto principal (Central), como <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>Se você voltar no projeto Central, alguns commits atrás, vai encontrar exatamente esse mesmo arquivo como <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>. E a principal vantagem dessa abordagem é justamente conseguir mover a maior parte do código para fora do projeto principal, junto com os specs, para uma gem de dependência, sem precisar &ldquo;reescrever&rdquo; nada.</p>
<p>Se eu tivesse que reescrever todo ou um pedaço grande do código, teria sido uma escolha bem mais cara e eu provavelmente teria adiado isso para outro momento e focado em features mais valiosas primeiro.</p>
<p>Essa abordagem está longe de perfeita, mas saiu super barata. Consegui mover toda a lógica de negócio relevante para fora do projeto principal sem precisar reescrever nada além de algum código de &ldquo;fiação&rdquo;. A nova gem de dependência recebeu todas as partes relevantes e os specs, e tudo simplesmente roda.</p>
<p>Então, se você tem 2 ou mais aplicações Rails que poderiam compartilhar os mesmos models, é assim que você pode começar. Claro, sempre tem um monte de ressalvas para manter em mente.</p>
<p>No meu caso, o projeto Central é o que pode ler e escrever no banco. Minha aplicação secundária interna usa os models apenas como read-only. Quando 2 aplicações diferentes escrevem no mesmo banco, você pode ter vários conflitos para lidar.</p>
<p>Essa abordagem é útil quando sua aplicação secundária se parece com um dashboard de Administração, por exemplo. Você precisa ter algumas das mesmas associações, scopes, e até validações para edição eventual, mas isso fica limitado a poucos usuários controlados.</p>
<p>Também é útil quando você está fazendo análise de dados, e novamente pode usar as mesmas associações, scopes, para construir relatórios e dashboards. Essencialmente, quando você precisa de acesso read-only, é decisão fácil.</p>
<p>No <a href="http://www.akitaonrails.com/2016/10/03/compartilhando-models-entre-apps-rails-parte-2"target="_blank" rel="noopener">próximo artigo</a> eu vou explicar como liguei uma aplicação secundária, usando a gem central-support e lidando com 2 bancos de dados diferentes ao mesmo tempo.</p>
]]></content:encoded><category>rails</category><category>arquitetura-de-software</category><category>testes</category></item><item><title>O Obrigatório "Por Que Elixir?" - Visão Pessoal</title><link>https://akitaonrails.github.io/2015/12/01/o-obrigatorio-por-que-elixir-visao-pessoal/</link><guid isPermaLink="true">https://akitaonrails.github.io/2015/12/01/o-obrigatorio-por-que-elixir-visao-pessoal/</guid><pubDate>Tue, 01 Dec 2015 13:26:00 -0200</pubDate><description>&lt;p&gt;Pois é, venho estudando e exercitando bastante com Elixir. O José Valim anunciou recentemente as novidades do futuro &lt;a href="https://twitter.com/josevalim/status/670595716776116224"target="_blank" rel="noopener"&gt;Elixir 1.2&lt;/a&gt;. O design da linguagem já é elegante, enxuto, e continua se polindo aos poucos.&lt;/p&gt;
&lt;p&gt;Antes de mais nada, sou Desenvolvedor de Aplicações Web. Lido com aplicações Ruby on Rails e infraestrutura. Portanto, não sou desenvolvedor Desktop, Mobile, de Games ou de Ferramentas. Isso é muito importante deixar claro logo de cara.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Pois é, venho estudando e exercitando bastante com Elixir. O José Valim anunciou recentemente as novidades do futuro <a href="https://twitter.com/josevalim/status/670595716776116224"target="_blank" rel="noopener">Elixir 1.2</a>. O design da linguagem já é elegante, enxuto, e continua se polindo aos poucos.</p>
<p>Antes de mais nada, sou Desenvolvedor de Aplicações Web. Lido com aplicações Ruby on Rails e infraestrutura. Portanto, não sou desenvolvedor Desktop, Mobile, de Games ou de Ferramentas. Isso é muito importante deixar claro logo de cara.</p>
<p>E, a propósito, conforme vou focando meus esforços de programação cada vez mais em Elixir, isso não significa que estou &ldquo;trocando&rdquo; de Ruby para Elixir. Não preciso fazer escolhas exclusivas, e na minha cabeça, ao menos por um período de tempo, Rails unindo forças com Phoenix vai ser um combo muito difícil de bater para minhas estratégias de desenvolvimento web.</p>
<p>Eu sei, Phoenix é construído como o Rails, então por que não trocar de vez: porque a maior parte dos sites baseados em conteúdo não precisa dos aspectos de concorrência do Phoenix, e eu falei &ldquo;por um período de tempo&rdquo; porque o Rails ainda tem um ecossistema gigantesco com gems mais maduras que tornam o desenvolvimento mais fácil e mais rápido. Isso pode mudar no futuro, mas por enquanto o combo faz sentido porque eu posso construir um site normal em Rails como faria normalmente (com Devise, ActiveAdmin, Spree ou o que for) e adicionar Phoenix para coisas como WebSockets (notificações em tempo real, chat em tempo real, background jobs que podem rodar de forma mais eficiente do que o Sidekiq, etc).</p>
<p>Esse artigo vai resumir minha visão pessoal em 2 frentes:</p>
<ul>
<li><a href="#developer-roles">Outras Funções de Desenvolvedor além da Web</a></li>
<li><a href="#functional-concepts">Conceitos &ldquo;Funcionais&rdquo; que Realmente Importam</a>
<ul>
<li><a href="#immutability">Imutabilidade e Passagem de Mensagens Opacas são MUITO importantes</a></li>
<li><a href="#coroutines">Coroutines e Schedulers</a></li>
<li><a href="#static-dynamic">Tipagem Estática vs Dinâmica ainda é controverso</a></li>
<li><a href="#fault-tolerance">Tolerância a Falhas: Não tenha medo do seu código</a></li>
</ul>
</li>
<li><a href="#summary">Resumo</a></li>
</ul>
<p>Como qualquer coisa sobre a qual eu queira argumentar, isso é <strong>longo</strong>, mas não seria legal apenas afirmar algo sem elaborar. Nessa busca por entendimento, eu posso ter confundido uma coisa ou outra, então me avisem na seção de comentários abaixo se houver coisas para corrigir ou explicar com mais detalhes.</p>
<p>Vamos começar.</p>
<p><a name="developer-roles"></a></p>
<h3>Outras Funções de Desenvolvedor além da Web<span class="hx:absolute hx:-mt-20" id="outras-funções-de-desenvolvedor-além-da-web"></span>
    <a href="#outras-fun%c3%a7%c3%b5es-de-desenvolvedor-al%c3%a9m-da-web" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Num ambiente Desktop, você definitivamente vai querer fazer uma combinação de <a href="https://github.com/nwjs/nw.js/"target="_blank" rel="noopener">Node-Webkit</a> com bibliotecas nativas. Se você está em ambientes corporativos específicos, não vai ter outras escolhas além do desenvolvimento .NET baseado em WFC ou Java Swing. Suas opções estão definidas há um bom tempo, e até o Visual Basic.NET ainda tem seu lugar. Toolchains específicas vão ser ditadas pela Microsoft e Oracle/Java Community Process.</p>
<p>Em ambientes Linux você ainda vai usar wrappers em torno de GTK+, Qt ou toolkits similares. Não há muitas saídas aqui.</p>
<p>Se você é um desenvolvedor Mobile-first, você <strong>precisa</strong> aprender o caminho do Swift e Objective-C (até certo ponto) para iOS e o sabor específico de Java para o Dalvik/ART do Android. Mas eu vou argumentar que você tem muito a ganhar no desenvolvimento nativo se usar <a href="http://www.rubymotion.com/"target="_blank" rel="noopener"><strong>RubyMotion</strong></a>. Ou, você pode simplesmente acompanhar o esforço do Facebook com <a href="https://facebook.github.io/react-native/"target="_blank" rel="noopener">React Native</a>. Há muita fragmentação nesse ambiente, você pode fazer o mínimo possível com Web Mobile e Phonegap/Cordova, e construir apps interessantes com ferramentas como o Ionic Framework. O único consenso é que se você realmente quer construir o que há de mais avançado da próxima geração, vai querer mergulhar fundo nos frameworks nativos que cada plataforma oferece.</p>
<p>Se você é Desenvolvedor de Games você quer estar o mais próximo possível do metal. É perfeitamente viável escrever jogos mobile jogáveis usando qualquer número de bibliotecas Javascript junto com tecnologias do HTML 5 como Canvas e WebGL. Mas para o blockbuster da próxima geração você vai usar engines maduras como Unity ou Unreal ou até construir a sua própria se estiver realmente investido nesse campo. Isso vai te forçar a saber programar em C/C++. Realmente depende de quão fundo você quer ir na toca do coelho.</p>
<p>Se você é Desenvolvedor de Ferramentas vai ter os benefícios tanto da geração antiga quanto da nova de linguagens. Se você está mais perto do Kernel do Linux vai precisar mesmo de C/C++ na bagagem. Mas para a nova geração de containers leves (LXC), Docker, você pode se beneficiar de <strong>Go</strong>, um ambiente de desenvolvimento de aplicações talhado para tornar a vida mais fácil do que ter que lidar com as idiossincrasias de C/C++. <strong>Rust</strong> é outra excelente escolha nova para tornar mais fácil - entre outras coisas - escrever bibliotecas e ferramentas pequenas livres de vazamentos de memória (estou mencionando isso em particular porque é importante para linguagens como Ruby ou Python conseguirem adicionar performance fazendo binding com bibliotecas nativas em C, e o Rust facilita essa tarefa).</p>
<p>Linguagens diferentes têm times diferentes e objetivos de longo prazo diferentes, por isso comparar linguagens não é como comparar maçãs e laranjas. Go, por exemplo, é mais pesado que Rust, mas ambos são bons para ferramentas de linha de comando, daemons especializados, e no caso de Go, empreitadas pesadas em rede e concorrência.</p>
<p>Aos meus olhos, Go é um Java ou C++ &ldquo;melhor&rdquo;. E não interpretem mal: Java ainda é uma linguagem e plataforma muito boa. Há pouquíssimas coisas que chegam perto da maturidade da JVM e do extenso ecossistema por trás dela. Eu não pensaria nem por um segundo em tentar reescrever sistemas complexos escritos em Java como a biblioteca Lucene ou as soluções Elasticsearch/SOLR, por exemplo.</p>
<p>Mas o poder do Java padrão é difícil de ser desencadeado sem um aquecimento para o HotSpot pegar embalo. Isso faz dele uma solução ruim para ferramentas de linha de comando. Mas agora você tem um bom meio-termo com Go. Você também não tem uma boa experiência embarcando Java em outras plataformas, e aí você precisaria voltar para C, mas agora você tem outro bom meio-termo com Rust.</p>
<p>Se você quer desencadear modelos diferentes de programação, especialmente os mais adequados a abstrações concorrentes como o modelo de Atores tipo <a href="https://en.wikipedia.org/wiki/Actor_model_and_process_calculi_history"target="_blank" rel="noopener">CSP do Hoare</a>, você pode tentar <strong>Scala</strong> com <a href="http://akka.io/"target="_blank" rel="noopener">Akka</a> (que agora é a biblioteca padrão de atores) e o <a href="http://blog.paralleluniverse.co/2013/05/02/quasar-pulsar/"target="_blank" rel="noopener">Pulsar/Quasar</a> do <strong>Clojure</strong>. Akka e Quasar são os que chegam &ldquo;perto&rdquo; (mas nunca conseguem igualar) a plataforma OTP nativa do Erlang.</p>
<p>Para a Web no geral, você se vira muito bem com o ecossistema atual (em constante mudança, instável) Node.js, Python (Django, Plone), Ruby (Ruby on Rails, Sinatra), PHP (Zend, Laravel), até Perl tem seu lugar. Combinado com serviços maduros em diferentes plataformas (Elasticsearch em Java, PostgreSQL em C, RabbitMQ em Erlang), qualquer aplicação web grande e complexa pode ser escrita com qualquer combinação das melhores ferramentas que melhor atendam às suas necessidades.</p>
<p>Essa é uma visão injusta e curta, claro. Eu não cobri todos os aspectos da ciência da computação ou da indústria. Existem várias outras linguagens ativas e úteis como Lua, Haskell, Fortran, Ada, Julia, R. A mensagem é: você não precisa escolher uma única linguagem, vai realmente depender do que você vai entregar. E um verdadeiro artesão vai dominar muitas ferramentas para fazer o trabalho da melhor maneira possível.</p>
<p><a name="functional-concepts"></a></p>
<h3>Conceitos &ldquo;Funcionais&rdquo; que Realmente Importam<span class="hx:absolute hx:-mt-20" id="conceitos-funcionais-que-realmente-importam"></span>
    <a href="#conceitos-funcionais-que-realmente-importam" 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">Eu escrevi sobre minhas opiniões</a> sobre a atual onda do chamado estilo Funcional de programação. Recomendo que leia antes de continuar.</p>
<p>Há alguns aspectos que são intransponíveis se você realmente quer ir além da pesquisa acadêmica e entrar no mundo real da produtividade.</p>
<p><a name="immutability"></a></p>
<h4>Imutabilidade e Passagem de Mensagens Opacas são MUITO importantes<span class="hx:absolute hx:-mt-20" id="imutabilidade-e-passagem-de-mensagens-opacas-são-muito-importantes"></span>
    <a href="#imutabilidade-e-passagem-de-mensagens-opacas-s%c3%a3o-muito-importantes" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>Para que computação seja rápida, estamos acostumados a compartilhar dados entre rotinas. A gente movimenta ponteiros e altera dados no lugar.</p>
<p>Não é particularmente rápido tornar as coisas imutáveis e não compartilhadas. Quanto mais você torna os dados mutáveis e quanto mais compartilha, mais difícil fica fazer seu código rodar concorrentemente.</p>
<p>É um <strong>trade-off</strong> importante: se você vê dados mutáveis e estado compartilhado, está otimizando para performance.</p>
<p>Não dá para dizer que uma linguagem é essencialmente boa ou ruim por ter dados mutáveis ou imutáveis. Mas, valha o que valer, minha opinião pessoal é que é mais difícil convencer usuários a seguir convenções como <em>&ldquo;compartilhe o mínimo possível, mute o mínimo possível&rdquo;</em>. A maioria nem vai saber sobre isso se não for built-in e forçado. Eu prefiro ter imutabilidade forçada por padrão.</p>
<p>Em Erlang, dados são <strong>imutáveis</strong>. Similar a Java, ele <a href="http://erlang.org/pipermail/erlang-questions/2013-March/072760.html"target="_blank" rel="noopener">passa valores por referência</a> em chamadas de rotinas (não está copiando valores entre chamadas, como muitos confundem).</p>
<p>E no caso da recursão ele otimiza através do <a href="http://learnyousomeerlang.com/recursion"target="_blank" rel="noopener">Tail Call Optimization</a> para deixar mais rápido. A propósito, essa é uma otimização que a JVM ainda não consegue fazer direito. Clojure precisa de chamadas especiais &lsquo;recur&rsquo; e &rsquo;trampoline&rsquo;, por exemplo. Scala consegue reescrever a recursão de cauda para um loop em tempo de compilação, com a anotação &lsquo;@tailrec&rsquo;. <a href="http://ferd.ca/erlang-s-tail-recursion-is-not-a-silver-bullet.html"target="_blank" rel="noopener">Erlang tem suas próprias armadilhas</a> também, então não é tão preto no branco no momento.</p>
<p>Em Erlang, como expliquei antes, você roda funções dentro de processos completamente isolados. Se a função recorre ou bloqueia, ela permanece isolada. Processos só conseguem se comunicar enviando mensagens (imutáveis e opacas) uns aos outros. As mensagens são enfileiradas em uma &ldquo;run queue&rdquo; ou &ldquo;mailbox&rdquo; e a função pode escolher receber e responder a essas mensagens. É isso.</p>
<p>Então, você pode passar valores por referência entre rotinas, ou pode compartilhar dados em um processo terceiro como mediador dos dados. Uma dessas infraestruturas embutidas no Erlang é o <strong>ETS</strong>, o <a href="http://elixir-lang.org/getting-started/mix-otp/ets.html"target="_blank" rel="noopener">Erlang Term Storage</a>, que é parte da chamada plataforma OTP. Pense no ETS como um armazenamento chave-valor embutido muito simples e muito rápido como o Memcached. Você usa ele para os mesmos casos de uso de um cache e é tão simples quanto fazer assim:</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>Muitos podem argumentar que o <strong>isolamento rígido de processos</strong> do Erlang e a comunicação restrita estritamente à passagem de mensagens opacas é exagero e que dá para passar usando algo parecido com a <a href="http://clojure.org/refs"target="_blank" rel="noopener">MVCC Software Transaction Memory</a> do Clojure, ou STM. Você até tem STM no Erlang, com a outra ferramenta nativa do OTP, construída em cima do ETS, chamada <strong>Mnesia</strong>. Ela oferece o equivalente a transações ACID de banco de dados em memória. Não é um conceito novo, mas STM não está disponível como funcionalidade de linguagem e ainda é incerto se realmente é uma boa escolha tê-lo.</p>
<p>Um resultado inspirado, eu acredito, da escolha do Clojure de ter memória transacional com fila de histórico e isolamento de snapshots é mostrado em sua joia da coroa, <a href="http://www.datomic.com/"target="_blank" rel="noopener">Datomic</a>. A ideia não é revolucionária de jeito nenhum, já que você tem muitos outros <a href="http://www.xaprb.com/blog/2013/12/28/immutability-mvcc-and-garbage-collection/"target="_blank" rel="noopener">trabalhos prévios</a> como RethinkDB, CouchDB, e extensões para bancos de dados existentes. Bom para um serviço, ainda assim eu não acho que seja uma boa coisa compartilhar estado, mesmo que você tenha um transactor em volta desse estado. A imutabilidade do Erlang com isolamento rígido de processos ainda não tem páreo.</p>
<p><a name="coroutines"></a></p>
<h4>Coroutines e Schedulers<span class="hx:absolute hx:-mt-20" id="coroutines-e-schedulers"></span>
    <a href="#coroutines-e-schedulers" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>Você já conhece sub-rotinas, faz isso o tempo todo particionando grandes porções de código em funções ou métodos menores que se chamam. Você talvez já conheça um tipo especializado de Coroutines na forma de <a href="http://bit.ly/1lVuLFJ"target="_blank" rel="noopener">Fibers</a> (como implementado pela primeira vez no Windows por volta de 1997).</p>
<p>Fibers oferecem um jeito da execução da sua função atual &ldquo;ceder&rdquo; (yield) de volta ao seu chamador, preservando seu estado atual, e então o chamador pode &ldquo;retomar&rdquo; (resume) a Fiber suspensa para continuar sua execução de onde parou da última vez. Isso permite multitasking cooperativo, não preemptivo. Temos Fibers em Python, Ruby e outras linguagens e isso permite a criação de construções como Generators. Até Javascript pode ter algum tipo de Fibers se você adicionar bibliotecas como <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>A chamada para &lsquo;yield&rsquo; suspende a execução atual até que a função no &lsquo;setTimeout&rsquo; seja chamada. Aí ela chama &lsquo;run&rsquo;, que retoma a função previamente cedida. Isso ainda é &ldquo;rudimentar&rdquo; comparado a coroutines: porque a própria função tem que ceder controle para o reactor event loop, no caso de uma aplicação Node.js. Se você não fizer isso, vai bloquear o event loop num processo Node.js single threaded, e portanto bloqueia tudo até a função terminar, derrotando todo o propósito. E essa é uma daquelas &ldquo;convenções&rdquo; que &ldquo;bons&rdquo; programadores deveriam seguir, mas a maioria vai esquecer.</p>
<p>Fibers são úteis para tornar menos feio programar num ambiente Reactor rudimentar, onde você depende de callbacks chamando callbacks e acaba com o anti-padrão da <a href="https://bjouhier.wordpress.com/2012/03/11/fibers-and-threads-in-node-js-what-for/"target="_blank" rel="noopener">pirâmide da perdição de callbacks</a>. Com Fibers você pode programar como faria numa linguagem imperativa síncrona, transformando esse código Javascript feio:</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>Nessa coisa mais gerenciável:</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>A confusão toda das <a href="https://en.wikipedia.org/wiki/Futures_and_promises"target="_blank" rel="noopener">Promises, Futures</a> depende em parte de Fibers adequadas. Javascript, sendo um design muito pobre, não vem com nada built-in e daí a proliferação de implementações de Fibers, Deferreds, Promises, Futures que nunca conseguem alcançar nem consenso nem pessoas realmente usando elas em larga escala.</p>
<p>Então, Fibers são ok. Coroutines são melhores porque você tem múltiplos pontos de suspender uma função e mais. E ainda melhor, em Erlang você nem precisa pensar em loops Reactor rudimentares (sim, reactors são uma construção rudimentar para concorrência quando você não tem outra boa escolha): tem <strong>chamadas assíncronas transparentes</strong>. Tudo em Erlang é assíncrono e não-bloqueante, mas você não lida com pirâmides de callback porque tem algo melhor por baixo: o <strong>Scheduler</strong>.</p>
<p>A propósito, eu acho muito frustrante que <a href="http://stackoverflow.com/questions/18058164/is-golang-goroutine-a-coroutine"target="_blank" rel="noopener">as &ldquo;goroutines&rdquo; do Go <strong>não são</strong> &ldquo;coroutines&rdquo; propriamente ditas</a>.</p>
<p>Para cada processo Erlang com suporte SMP (multiprocessamento simétrico) vai existir <strong>uma</strong> thread real por núcleo de CPU disponível no seu sistema, e para cada thread vai existir um único Scheduler para gerenciar as green threads internas (processos) e a run-queue.</p>
<p>Como programador, eu não preciso &ldquo;lembrar&rdquo; de ceder controle de volta a um event loop passivo. O scheduler vai cuidar de balancear o tempo de computação para cada processo concorrente. Se um processo estiver demorando demais, o Scheduler pode escolher suspendê-lo e dar tempo para outras rotinas. Erlang define uma <a href="http://erlang.org/pipermail/erlang-questions/2001-April/003132.html"target="_blank" rel="noopener">&ldquo;redução&rdquo;</a> e que existem diferentes níveis de prioridade de funções. Se uma função leva mais que 2.000 reduções, o Scheduler pode escolher suspendê-la. Se você tem 8 núcleos de CPU, mas a computação nos processos não é pesada, a VM pode escolher usar apenas 1 ou 2 Schedulers e deixar os outros 6 ociosos para que o hardware possa desligar os núcleos para economizar energia (!!). Isso mesmo, Erlang até é Eco Friendly!</p>
<p>E precisamos repetir isso de novo: porque cada processo é <strong>rigidamente isolado</strong>, com dados imutáveis e sem estado compartilhado, fica mais fácil suspender um processo em execução. No caso da JVM isso geralmente é implementado levantando exceções checadas e fazendo todo mundo implementar alguma interface Suspendable. Pode ser feito usando essa <a href="http://www.matthiasmann.de/content/view/24/26/"target="_blank" rel="noopener">biblioteca de continuation Java de terceiros</a> onde você cede levantando uma Exception (!) Coisa nojenta.</p>
<p>Rust <a href="https://github.com/rustcc/coroutine-rs"target="_blank" rel="noopener">ainda está implementando</a> algo para coroutines também, mas ainda nada tão maduro. Mas de novo, coroutines é apenas parte da história, você precisaria de um sistema mais pesado com schedulers em userland para fazer sentido. Go é um candidato melhor para incorporar tal sistema em seu runtime, mas também fica devendo em conseguir implementar tudo isso. Há o <a href="http://www.jerf.org/iri/post/2930"target="_blank" rel="noopener">Suture</a>, uma tentativa de ter parte do OTP em Go, mas não dá para ser feito. Até Akka, o primeiro clone OTP mainstream para Scala, não consegue chegar perto por causa das limitações da JVM. <a href="http://blog.paralleluniverse.co/2013/05/02/quasar-pulsar/"target="_blank" rel="noopener">Clojure com Pulsar/Quasar</a>, chega <strong>mais perto</strong>, mas ainda não chegou lá.</p>
<p>Agora, o Scheduler do Erlang não só é capaz de suspender e retomar processos, mas também cuida da passagem de mensagens entre eles. Então cada Scheduler tem sua própria run-queue para enfileirar e despachar mensagens. De novo, porque os dados são imutáveis, você só precisa de alguma forma de locking quando você quer outro Scheduler (em outra thread real) para tomar conta de alguns processos a fim de balancear o processamento entre os núcleos. Erlang suporta SMP <a href="http://erlang.org/pipermail/erlang-questions/2008-September/038231.html"target="_blank" rel="noopener">desde o OTP R12B</a> (estamos no R18 agora, e ainda evoluindo).</p>
<p>A maioria das linguagens ainda depende do modelo preemptivo de threads reais do SO para fazer multitasking. E isso é pesado e lento por causa de toda a troca de contexto envolvida e toda a lógica de locking que a maioria dos programadores vai fazer errado (a melhor prática para concorrência é: não use threads, é provável que você vá errar). De novo, fazemos as suposições corretas primeiro: programadores não conseguem fazer multithreading direito, então deixe um Scheduler fazer isso, quando necessário, e evitando trocas de contexto lentas do SO o máximo possível. Green threads suspendíveis combinadas com um Scheduler em userland coordenando troca cooperativa é uma escolha muito mais rápida e segura.</p>
<p>Se você quer aprender mais sobre coroutines, esse <a href="http://www.inf.puc-rio.br/~roberto/docs/corosblp.pdf"target="_blank" rel="noopener">paper de Lua</a> sobre o assunto explica em mais detalhes o que eu acabei de elaborar:</p>
<blockquote>
Implementar uma aplicação multitasking com coroutines de Lua é direto. Tarefas concorrentes podem ser modeladas por coroutines de Lua. Quando uma nova tarefa é criada, ela é inserida em uma lista de tarefas vivas. Um simples dispatcher de tarefas pode ser implementado por um loop que continuamente itera sobre essa lista, retomando as tarefas vivas e removendo as que terminaram seu trabalho (essa condição pode ser sinalizada por um valor predefinido retornado pela função main da coroutine ao dispatcher). Eventuais problemas de fairness, que são fáceis de identificar, podem ser resolvidos adicionando pedidos de suspensão em tarefas demoradas.
</blockquote>
<p><a name="static-dynamic"></a></p>
<h4>Tipagem Estática vs Dinâmica ainda é controverso<span class="hx:absolute hx:-mt-20" id="tipagem-estática-vs-dinâmica-ainda-é-controverso"></span>
    <a href="#tipagem-est%c3%a1tica-vs-din%c3%a2mica-ainda-%c3%a9-controverso" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>Vimos um movimento mais pesado de sistemas de tipagem estática burocráticos (principalmente Java antes do 6, C# antes do 4, C++) para linguagens puramente dinâmicas como Perl no fim dos anos 80, Python no fim dos 90, Ruby em meados de 2000. Tentamos sair do <em>&ldquo;deixar o compilador feliz&rdquo;</em> para o <em>&ldquo;deixar os programadores felizes&rdquo;</em>, o que faz mais sentido se você me perguntar.</p>
<p>Scala, Groovy, Haskell, Swift trouxeram um meio-termo bem prático com sistemas de <strong>Inferência de Tipos</strong> derivados de <a href="https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system"target="_blank" rel="noopener">Hindley-Milner</a>, de um jeito que podemos codar meio que como em linguagens dinâmicas mas com o compilador fazendo mais trabalho para inferir tipos para nós antes de gerar o bytecode executável final.</p>
<p>Mas tem um <strong>grande</strong> porém: é muito difícil fazer <strong>hot swap</strong> de código dentro do runtime se você tem assinaturas estáticas. Eu não estou dizendo que é impossível, mas é muito mais difícil. Você até consegue recarregar código em Java (um exemplo é o <a href="https://github.com/spring-projects/spring-loaded"target="_blank" rel="noopener">Spring Loaded</a>) ou Haskell (existe um plugin de hotswap e outras alternativas). Você não faz reloads granulares numa linguagem tipada estaticamente porque se quiser mudar a assinatura de um método, precisa mudar o grafo que depende dessa assinatura. É factível, embora chato.</p>
<p>Em Erlang, porque não há essas dependências duras, e de novo por causa das vantagens inerentes de só ter dados imutáveis sem estado compartilhado, e dependências limitadas a passagem de mensagens opacas, você consegue recarregar granularmente um único módulo e mais importante de tudo: você consegue implementar callbacks simples para <strong>transformar o estado antigo de um processo em uma nova estrutura de estado</strong>, porque só recarregar o código é metade da história se você vai ter centenas de processos antigos reiniciando no novo código mas tendo que lidar com estado anterior. Num GenServer Erlang, você só implementa esse único 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>Então, embora Inferência de Tipos seja um meio-termo legal, a flexibilidade da Tipagem Dinâmica vai além de ser fácil para programadores usarem. Como com Python, Ruby, Javascript, Perl e outras linguagens dinamicamente tipadas, você vai querer cobrir seu código com suítes de teste adequadas - o que não deveria ser opcional em linguagens fortemente tipadas de qualquer jeito. Não há dúvidas que a análise estática de um compilador ajuda muito, mas é minha visão pessoal que tipagem dinâmica me permite mais flexibilidade.</p>
<p><a name="fault-tolerance"></a></p>
<h3>Tolerância a Falhas: Não tenha medo do seu código<span class="hx:absolute hx:-mt-20" id="tolerância-a-falhas-não-tenha-medo-do-seu-código"></span>
    <a href="#toler%c3%a2ncia-a-falhas-n%c3%a3o-tenha-medo-do-seu-c%c3%b3digo" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Com Tipagem Dinâmica vamos acabar de novo na discussão sem fim de <em>&ldquo;programadores nunca fazem certo, precisamos de um compilador para forçar regras estáticas&rdquo;</em>. E você está quase certo: programadores erram, mas um compilador não vai te salvar de qualquer jeito, e pior: ele pode te dar uma falsa sensação de segurança. Não há buraco de segurança pior que falsa sensação de segurança. Uma suíte de testes é muito melhor para asseverar implementação adequada, e ainda assim não é garantia firme.</p>
<p>Eu disse que isso é controverso, já que um sistema de tipagem estática mais rígido é mais produtivo do que ter que testar unitariamente todos os tipos de input e output de toda função. Mas se você pensou isso, está pelo menos errado em que isso não é para o que servem os testes unitários: é para testar comportamentos de unidade, independente dos tipos. Testar tipos é o que chamamos de &ldquo;chicken typing&rdquo; e é outra forma de programação defensiva. Você deve testar comportamento, não coisas que um compilador checaria. E de novo, ter a tipagem estática trava minha flexibilidade porque agora eu tenho que lutar constantemente contra a tipagem, adicionar mais boilerplate, e em última análise se o comportamento está errado, o código está errado, apesar das checagens de tipo.</p>
<p>Para um sistema ser <strong>&ldquo;tolerante a código bugado ou falhas não capturadas&rdquo;</strong> é o oposto de poluir seu código com declarações de proteção como &ldquo;try/catch&rdquo; ou &ldquo;if err == x&rdquo; ou ter alguma forma de checagem em nível estático com Result monads (que a maioria das pessoas vai só desempacotar de qualquer jeito). Guards só vão até certo ponto. E sim, isso é anedótico já que não há estatísticas de que &ldquo;todo mundo vai desempacotar&rdquo; e a maioria dos bons programadores não vai, mas se a experiência me diz uma coisa é que programadores ruins vão cair no &ldquo;try/catch&rdquo; tudo quando eles não sabem o que fazer de qualquer maneira.</p>
<p>Você precisa de um sistema que permita que qualquer código bugado falhe e não derrube o ambiente em volta dele, o que o levaria a um estado inconsistente, corrompido.</p>
<p>O problema com código com defeito é que ele deixa o estado numa posição onde não tem para onde ir. E se esse estado for compartilhado, você deixa todo outro pedaço de código numa posição onde eles não conseguem decidir para onde ir em seguida. Você vai precisar desligar tudo e reiniciar a partir do último estado bom conhecido. Quase todos os fluxos de &ldquo;continuous delivery&rdquo; são implementados em torno de reiniciar tudo.</p>
<p>Em vez de ter que derrubar tudo caso você esqueça de fazer try/catch em alguma coisa, você pode confiar nos alicerces do Erlang de não compartilhar estado, ter dados imutáveis, e - o mais importante - ter o sistema de processos leves rigidamente isolados para fazer com que o processo que está segurando o código com defeito <strong>desligue e avise seu Supervisor</strong>. Essa é a ideia geral por trás do chamado OTP em Erlang.</p>
<p>A ideia de um Supervisor é ter um processo pequeno que monitora outros processos. O Supervisor tem o mínimo possível de código para raramente (ou nunca) falhar (o melhor tipo de código é &ldquo;nenhum código&rdquo;). Uma vez que um processo com defeito quebra por causa de exceção não capturada ou outras razões indeterminísticas, ele envia uma mensagem de notificação para o mailbox da run-queue do Supervisor, e então morre limpo. O Supervisor então escolhe o que fazer baseado em sua estratégia de restart subjacente.</p>
<p>Digamos que você tenha uma lista de URLs que está fazendo scraping. Mas você não antecipou estruturas sujas na sua lógica de parsing. O processo fazendo o scraping quebra e morre. O Supervisor é notificado e escolhe reiniciar o processo, dando ao novo processo o estado anterior - a lista de URLs - e agora que a URL com defeito não está mais ali, o novo processo pode continuar feliz o trabalho com a próxima URL da lista.</p>
<p>Esse é um exemplo simples da dinâmica entre a Erlang VM, o Supervisor e seus workers filhos. Você pode ir mais longe e ter várias Applications, que por sua vez sobem novos Supervisors, que por sua vez sobem processos filhos. E você tem uma Árvore de Supervisores que pode capturar exits e reiniciar bits granulares do seu código de runtime sem trazer o resto do sistema para um estado inconsistente.</p>
<p>Tal é a beleza do conceito de isolamento rígido de processos.</p>
<p>Toda I/O no sistema é embrulhada em <a href="http://www.erlang.org/doc/tutorial/c_port.html"target="_blank" rel="noopener">Ports</a>, que obedecem à lógica Async/callback transparentemente sem você ter que criar pirâmides de callbacks. O processo consumindo tais ports apenas é suspenso pelo Scheduler até que a chamada Async retorne e ele possa retomar o trabalho. Sem inferno de pirâmide de callback. Sem necessidade de implementações rudimentares de Fibers para permitir sistemas rudimentares de Promises/Futures. Apenas coroutines dentro de processos que podem ser suspensas e retomadas pelo Scheduler. Menos oportunidades para erros de programador se acumularem.</p>
<p>Então, um programador vai esquecer de codar todo possível ramo de execução e porque ele sabe disso, a <strong>pior coisa</strong> que pode acontecer não é se ele esquecer uma exceção não capturada, mas se ele decidir programar <strong>defensivamente</strong> e adicionar condições gerais para capturar <strong>qualquer</strong> erro e nunca levantá-lo. Você já viu antes, quando encontra código que está preso dentro de blocos try/catch genéricos, tentando evitar todo erro possível. Mas o que realmente acontece é que o sistema pode não quebrar, mas sua lógica e seu processamento estão com defeito no seu cerne. E você não vai descobrir, porque não quebra, portanto ninguém é nunca notificado! Você não vai acabar com menos bugs, vai acabar com uma montanha de bugs lógicos que nunca são percebidos porque são todos engolidos!!</p>
<p>Esse é o cerne da Tolerância a Falhas: não tenha medo do Erlang, tenha medo dos programadores! Em vez disso, devemos fazer o que Joe Armstrong apresentou em seu paper seminal <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>. Esse é tanto um guia detalhado para Erlang e OTP quanto seus argumentos sobre como escrever sistemas tolerantes a falhas fazendo o programador escrever código tão claramente quanto ele originalmente pretendia, sem a necessidade de ser defensivo, com a confiança de que se ele esquecer alguma coisa, OTP vai estar lá para capturar e não deixar o sistema morrer, mas dar uma chance de consertar e recarregar sem disturbar outras partes boas do sistema.</p>
<p>Esse é o objetivo último da boa programação: não ser defensivo, não colocar try/catch em todo lugar porque você está com medo.</p>
<p><a name="summary"></a></p>
<h3>Resumo<span class="hx:absolute hx:-mt-20" id="resumo"></span>
    <a href="#resumo" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Esse curto resumo é a razão pela qual Erlang soa muito atraente para minhas necessidades de Desenvolvimento Web, ou para qualquer sistema complexo escalável, ao menos para mim.</p>
<p>Toda outra linguagem teve que fazer trade-offs. Toda nova linguagem em cima da JVM tem que lidar com limitações inerentes a como a JVM foi originalmente arquitetada. Rust ainda pode construir abstrações melhores, mas o escopo dele é para ferramentas e bibliotecas menores, não sistemas complexos distribuídos. Sim, eventualmente ele pode fazer o que C/C++ pode, e a Mozilla está realmente baseando o núcleo da próxima geração de seu browser core em Rust. Isso vai dar bootstrap a bibliotecas e frameworks de mais alto nível melhores, da mesma forma que a Apple usando Objective-C criou todo o conjunto de Core frameworks que tornam implementar aplicações complexas muito mais fácil.</p>
<p>Go fez uma escolha por manter a familiaridade com sua herança C++. Claro, ele tem toneladas de funcionalidades úteis, em particular as goroutines built-in que tornam o código concorrente muito mais fácil que em linguagens anteriores.</p>
<p>Haskell é muito estrito para a maioria dos programadores (sim, Monads, ainda difícil para o programador médio compreender plenamente) e apesar de opiniões contrárias, para mim ele ainda parece apelar mais a pesquisadores do que a desenvolvedores do dia-a-dia. Outras linguagens dinâmicas como Ruby, Python, podem eventualmente ir pelo caminho de Erlang com Ruby adicionando alguma imutabilidade (<a href="https://bugs.ruby-lang.org/issues/11473"target="_blank" rel="noopener">na 2.3</a> com String imutável por padrão), mas ainda há um longo caminho a percorrer.</p>
<p>Erlang tem tudo, como Joe Armstrong vislumbrou que um sistema tolerante a falhas deveria ser. Começou como um exercício implementado em Prolog em 1986. Migrou do antigo compilador JAM para a atual VM BEAM em 1998. Adicionou SMP por volta de 2008. Tem evoluído gradualmente, polindo suas arestas, sendo realmente testado em batalha em sistemas realmente de missão crítica por décadas. Está pronto para nós, agora mesmo.</p>
<p>Tem Tolerância a Falhas garantida pelos princípios de dados imutáveis, sem estado compartilhado, passagem pura de mensagens opacas, e processos suspendíveis, todos gerenciados por Schedulers. Isso garante que rotinas com defeito podem quebrar um único processo mas não o sistema inteiro e definitivamente não trazer o estado de outros processos a estados inconsistentes, corrompidos.</p>
<p>Aí você pode instrumentar sua máquina virtual e checar que Supervisors estão reiniciando filhos mais do que você quer e decidir consertar o código com bug. E uma vez que conserta, você pode escolher <strong>não</strong> desligar e reiniciar o sistema inteiro para recarregar as correções de código, você pode fazer isso granularmente, on the fly, com processos rodando que vão pegar as correções uma vez que o Supervisor os reinicie. O hot swap granular de código é garantido porque não há hierarquia de tipos com a qual se preocupar.</p>
<p>E porque você tem coroutines adequadas sem hierarquia de dependências de estado compartilhado, você pode ter <strong>exceções assíncronas</strong> que podem forçosamente desligar processos sem criar efeitos colaterais a outros processos rodando. Um Supervisor pode escolher reiniciar toda sua lista de filhos quando um de seus filhos quebra e outro de seus filhos dependia desse processo anterior. Você pode trocar de estratégia de restart &ldquo;one for one&rdquo; para &ldquo;one for all&rdquo; (como os Mosqueteiros).</p>
<p>Há apenas um problema com Erlang: ele não foi desenhado para &ldquo;felicidade do programador&rdquo;, um conceito que nos acostumamos a ter como certo por causa do Ruby e linguagens mais novas.</p>
<p>Erlang tem suas raízes em Prolog e isso aparece. Uma vez que você se aprofunda e realmente exercita com a linguagem você possivelmente consegue se acostumar com ele. Mas se você veio de linguagens dinâmicas mais modernas como Ruby, Python, Groovy, com certeza vai sentir falta das construções modernas confortáveis.</p>
<p>Elixir é a peça que faltava, a Pedra Filosofal se você quiser, que pode destrancar todos os 30 anos de refinamentos, maturidade, tecnologias industriais testadas em batalha em larga escala, para o programador médio.</p>
<p>Ele traz muitas construções modernas como tornar macros mais fáceis para permitir Domain Specific Languages, ter comentários testáveis no seu código, adicionar uma biblioteca padrão mais moderna que é facilmente reconhecível do ponto de vista de Ruby ou Clojure, polimorfismo via Protocols, e por aí vai.</p>
<p>Esse é um exemplo de Elixir direto dos testes do código fonte:</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>Código que pode ser escrito assim automaticamente me faz sorrir.</p>
<p>Essa é a combinação perfeita. De novo, não é sem deficiências. Erlang de jeito nenhum é uma linguagem rápida. É bem mais rápido que Ruby, Python, ou outras linguagens interpretadas. Pode ser feito um pouco mais rápido com compilação nativa através do compilador HIPE, mas ainda assim nem perto das velocidades de Go, Rust, ou qualquer derivado de Java como Scala, Groovy ou Clojure.</p>
<p>Então, se você realmente precisa de poder bruto de computação, vai querer Go ou Java. De novo, eu não estou dizendo que existem só esses 2, mas são as escolhas usuais se você não quer descer para C/C++. Haskell tem performance excelente, mas a curva de aprendizado dele está longe de ser estelar.</p>
<p>Erlang é um sistema inteiro, tem seu próprio sistema de scheduling, controla processos vivos, respirando, cada um com seu próprio garbage collector, controla captura de sinais do sistema e por aí vai. Foi desenhado para ser um servidor completo. É muito menor que um container Java Enterprise Edition completo, tão pequeno que você consegue empacotar ferramentas de linha de comando que sobem rápido o bastante. Mas esse não é o ponto forte. Para esse propósito você se sai melhor com Go ou até Rust.</p>
<p>Pelas mesmas razões ele não foi feito para ser uma linguagem embutível do jeito que Lua é. Não foi feito para criar bibliotecas que possam ser facilmente lincadas via FFI ou exports de função tipo C, do jeito que Rust pode.</p>
<p>Há jeitos de criar aplicações classe-desktop, especialmente com <a href="http://www.erlang.org/doc/apps/wx/chapter.html"target="_blank" rel="noopener">wxWidgets</a> cross-platform, do jeito que a aplicação built-in Observer de instrumentação é feita, mas Erlang não foi construído para ser um toolkit desktop.</p>
<p>Também porque ele prioriza correção, processos rigidamente isolados se comunicando só por mensagens opacas, estados imutáveis e não compartilhados, isso significa que Erlang não é adequado para processamento pesado de ciência de dados. Então duvido que seja a melhor escolha para analytics de Big Data, sequenciamento de DNA e outras coisas pesadas em que ferramentas como Julia, R, Fortran, são escolhas melhores. Não é a mesma coisa que dizer que ele não pode ser um bom núcleo de banco de dados, Riak e CouchDB já provaram isso. Mas queries complexas em cima de altos volumes de dados também não é o ponto forte.</p>
<p>Então, Erlang é bom para sistemas distribuídos, com alta concorrência de troca de mensagens opacas e proxying. O cenário exato em que a Web está. Aplicações Web com carga pesada de throughput que precisam de chats e notificações em tempo real, transações de pagamento pesadas e demoradas, coleta de dados de muitas fontes a fim de reduzi-los a respostas HTML ou JSON consumíveis.</p>
<p>Mas para o desenvolvedor Web médio (e por &ldquo;médio&rdquo; eu quero dizer minimamente capaz de arquitetar os tipos de sistemas complexos com que lidamos todo dia em desenvolvimento web, não construção simples de site estático), Erlang era um desafiante real, e agora podemos ter o conforto de uma linguagem moderna real com toques de Ruby e Clojure, sem as complexidades da tipagem forte mas com a segurança das construções built-in de Tolerância a Falhas para entregar aplicações Web modernas, altamente confiáveis e altamente escaláveis.</p>
]]></content:encoded><category>elixir-e-erlang</category><category>sistemas-distribuidos</category><category>arquitetura-de-software</category></item><item><title>"Yocto Services"! E o Meu Primeiro Mês com Elixir!</title><link>https://akitaonrails.github.io/2015/11/25/yocto-services-e-meu-primeiro-mes-com-elixir/</link><guid isPermaLink="true">https://akitaonrails.github.io/2015/11/25/yocto-services-e-meu-primeiro-mes-com-elixir/</guid><pubDate>Wed, 25 Nov 2015 18:04:00 -0200</pubDate><description>&lt;p&gt;Cara, no último mês (quase, de 27 de outubro a 25 de novembro) eu decidi que já passou da hora de mergulhar fundo e aprender Elixir de verdade. E foi exatamente o que eu fiz. Ainda sou iniciante, mas já me sinto bem confiante de que consigo encarar projetos em Elixir agora.&lt;/p&gt;
&lt;p&gt;O meu processo de aprendizado:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Li toda a &lt;a href="http://elixir-lang.org/getting-started/introduction.html"target="_blank" rel="noopener"&gt;Documentação oficial do Elixir&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;Li o livro &lt;a href="https://pragprog.com/book/elixir/programming-elixir"target="_blank" rel="noopener"&gt;Programming Elixir&lt;/a&gt; do Dave Thomas inteiro;&lt;/li&gt;
&lt;li&gt;Assisti a quase todos os screencasts do &lt;a href="http://elixirsips.com/"target="_blank" rel="noopener"&gt;Elixir Sips&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;Li o livro &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; do Benjamin Tan Wei Hao inteiro;&lt;/li&gt;
&lt;li&gt;Fiz uma porção de tutoriais e exercícios.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;E, finalmente, documentei tudo o que aprendi quase todo dia nos seguintes artigos:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Cara, no último mês (quase, de 27 de outubro a 25 de novembro) eu decidi que já passou da hora de mergulhar fundo e aprender Elixir de verdade. E foi exatamente o que eu fiz. Ainda sou iniciante, mas já me sinto bem confiante de que consigo encarar projetos em Elixir agora.</p>
<p>O meu processo de aprendizado:</p>
<ol>
<li>Li toda a <a href="http://elixir-lang.org/getting-started/introduction.html"target="_blank" rel="noopener">Documentação oficial do Elixir</a>;</li>
<li>Li o livro <a href="https://pragprog.com/book/elixir/programming-elixir"target="_blank" rel="noopener">Programming Elixir</a> do Dave Thomas inteiro;</li>
<li>Assisti a quase todos os screencasts do <a href="http://elixirsips.com/"target="_blank" rel="noopener">Elixir Sips</a>;</li>
<li>Li o livro <a href="https://www.manning.com/books/the-little-elixir-and-otp-guidebook"target="_blank" rel="noopener">The Little Elixir &amp; OTP Guidebook</a> do Benjamin Tan Wei Hao inteiro;</li>
<li>Fiz uma porção de tutoriais e exercícios.</li>
</ol>
<p>E, finalmente, documentei tudo o que aprendi quase todo dia nos seguintes artigos:</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/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/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/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/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/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/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/2015/11/25/elixir-101-apresentando-a-sintaxe"target="_blank" rel="noopener">Elixir 101 - Introducing the Syntax</a></li>
</ul>
<p>Sim, eu sou prolífico e bastante focado. Estudei e escrevi tudo isso em menos de um mês (teria sido bem menos se eu pudesse ter usado as noites e finais de semana). Então eu diria que o desenvolvedor médio levaria pelo menos 3 a 4 meses para cobrir o mesmo material.</p>
<h3>Pensamento Inicial: Yocto Services!<span class="hx:absolute hx:-mt-20" id="pensamento-inicial-yocto-services"></span>
    <a href="#pensamento-inicial-yocto-services" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Para o bem ou para o mal, estamos no alvorecer da arquitetura de Micro Serviços. Resumidamente, é fragmentar o seu monolito em aplicações menores que respondem a endpoints HTTP - que as pessoas chamam de &ldquo;APIs&rdquo; - e que são responsáveis por um conjunto bem restrito de responsabilidades. Aí você cria uma aplicação web &ldquo;front-end&rdquo; que vai consumir esses serviços, como analytics, métodos de pagamento, diretórios de autenticação de usuários, e por aí vai.</p>
<p>Isso não é novidade nenhuma, claro. Ter APIs HTTP que devolvem estruturas JSON é só uma forma mais bonitinha de fazer os bons e velhos <strong>Remote Procedure Calls</strong>, ou RPCs, uma tecnologia que temos há décadas para interconectar clientes e servidores numa rede. Mas estou divagando.</p>
<p>Se isso é o que as pessoas chamam de serviços &ldquo;Micro&rdquo;, eu penso nos processos do Elixir como <strong>&ldquo;Yocto&rdquo;</strong> Services! (Milli &gt; Micro &gt; Nano &gt; Pico &gt; Femto &gt; Atto &gt; Zepto &gt; Yocto, aliás - eu posso ter acabado de inventar um novo termo aqui!)</p>
<p>Eu já descrevi um pouco da infraestrutura de Processos, como dar spawn neles, como trocar mensagens entre eles e como linkar eles. Então, vai lá ler <a href="http://www.akitaonrails.com/2015/11/22/observando-processos-em-elixir-the-little-elixir-otp-guidebook"target="_blank" rel="noopener">meu post anterior</a> se ainda não leu.</p>
<p>Dentro de uma aplicação Elixir você vai encontrar muitos processos, alguns da própria VM tocando o show e outros da sua própria aplicação. Se você arquitetou direito, implementou uma aplicação OTP, com grupos próprios de Supervisors e Children, todos organizados numa Supervision Tree. Um pequeno worker morre, o supervisor dele sabe como lidar com isso.</p>
<p>Agora, o modelo mental é o seguinte: pense num processo Elixir como um pequeno micro-serviço - um <strong>Yocto service</strong>, se preferir! - dentro da sua aplicação. Numa aplicação Phoenix, por exemplo, você não importa uma &ldquo;biblioteca de banco de dados&rdquo;, você na verdade dá start num &ldquo;Database Service&rdquo; (que é um Ecto Repo) que roda em paralelo com a aplicação Endpoint que responde aos requests HTTP que vêm da internet. O código nos seus controllers e models &ldquo;consome&rdquo; e envia mensagens para o &ldquo;Service&rdquo; Ecto Repo. É assim que você consegue visualizar o que está acontecendo.</p>
<p>Eu mostrei o Observer no artigo anterior também. Você vai encontrar uma árvore grande se abrir ele dentro do shell IEx de uma aplicação Phoenix:</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>Resumindo, você vai encontrar a seguinte seção na Supervision Tree do Phoenix App Pxblog:</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>Esse é o Pxblog que eu expliquei no <a href="http://www.akitaonrails.com/2015/11/20/phoenix-15-minute-blog-comparacao-com-ruby-on-rails"target="_blank" rel="noopener">artigo de comparação entre Phoenix e Rails</a> que publiquei poucos dias atrás.</p>
<p>Eu ainda não li o código fonte do Phoenix, mas se eu estou interpretando o Observer corretamente, o Endpoint.Server controla um pool de processos que são listeners TCP que a aplicação está pronta para aceitar requests, concorrentemente, com overflow para aceitar mais conexões (acredito que seja uma implementação de pool tipo Poolboy, que eu expliquei na <a href="http://www.akitaonrails.com/2015/11/19/ex-manga-downloadr-parte-2-poolboy-ao-resgate"target="_blank" rel="noopener">Parte 2 do artigo do Ex Manga Downloader</a>).</p>
<p>Aí você tem as aplicações PubSub.Supervisor e PubSub.Local que eu acredito que dão suporte aos canais WebSocket.</p>
<p>Sozinho, o Repo controla 10 processos iniciais no pool dele, possivelmente um pool de conexões com o banco de dados. Repare como os grupos Endpoint e Repo estão em ramos paralelos da árvore de supervisão. Se o Repo falha por causa de algum problema externo do banco, o grupo do Endpoint não precisa falhar junto. Isso é o que está declarado na definição da Application do Pxblog em <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>Veja como ele define o Endpoint e o Repo debaixo do Pxblog.Supervisor.</p>
<p>Eu posso ir lá e matar à força o nó inteiro do Pxblog.Repo da Supervision Tree usando o Observer, como fiz no artigo anterior, e a estratégia certa entra em ação, o Phoenix Supervisor reinicia o Repo com sucesso, e ninguém vai perceber que algo quebrou por baixo dos panos.</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>Do IEx eu ainda consigo fazer mais chamadas para o Repo desse jeito e ele responde como se nunca tivesse caído:</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>E a forma como eu penso sobre isso é assim: o meu shell IEx está mandando uma mensagem para o Yocto Service chamado Pxblog.Repo (na verdade ele encaminha mensagens para o adapter de banco de dados que aí faz o checkout de um processo do pool). Exatamente como eu consumiria Micro Serviços externos via APIs HTTP.</p>
<p>Então o panorama da sua aplicação é composto por uma série de processos e grupos de processos supervisionados, todos trabalhando para compor uma estrutura maior. Como eu disse em artigos anteriores, se um grupo de processos colapsar, o Supervisor dele entra em ação, captura o erro e usa a sua estratégia para, por exemplo, reiniciar todos os processos filhos, trazendo a aplicação de volta para um estado consistente, e sem você ter que reiniciar a aplicação Elixir inteira.</p>
<p>Então cada processo pode ser um Yocto Service completo, rodando online e esperando outros serviços consumirem ele, como os workers do Repo.</p>
<h3>Dividir e Conquistar<span class="hx:absolute hx:-mt-20" id="dividir-e-conquistar"></span>
    <a href="#dividir-e-conquistar" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>De novo, como disclaimer, ainda sou novo em Elixir, mas a forma que eu acho mais fácil de entender é assim:</p>
<ul>
<li>
<p>Se você precisa lidar com recursos externos, seja um arquivo, conexões de rede ou qualquer coisa fora da Erlang VM, você vai querer que isso seja um GenServer.</p>
</li>
<li>
<p>Aí, se você tem um GenServer, você quer dar start nele debaixo de um Supervisor (geralmente, o boilerplate simples que define os filhos e a estratégia de restart).</p>
</li>
<li>
<p>O número de processos GenServer que você quer iniciar depende de quantos processos paralelos você quer ter rodando. Por exemplo, se for um serviço de banco de dados, não tem sentido iniciar mais do que o número máximo de conexões disponíveis que o banco permite. Se você tem vários arquivos para processar, você quer no máximo o número de arquivos disponíveis ou - na prática - só alguns processos para lidar com lotes de arquivos. Geralmente você vai querer um pool de processos GenServer e, nesse caso, você quer usar Poolboy.</p>
</li>
<li>
<p>Um GenServer pode chamar outros GenServers. Você não quer usar mecanismo de tratamento de exceção do tipo try/catch porque o que você quer é justamente que aquele processo GenServer específico crashe se algo der errado: se o arquivo está corrompido, ou não existe, ou se a rede ficou instável ou caiu. O Supervisor vai substituir aquele processo por um GenServer novo no lugar dele e reabastecer o Pool se for preciso.</p>
</li>
<li>
<p>Você pode fazer GenServers <a href="http://www.akitaonrails.com/2015/11/25/exercicio-exmessenger-entendendo-nodes-em-elixir"target="_blank" rel="noopener">conversarem remotamente</a> usando o recurso de Node que eu expliquei 2 posts atrás, com o exemplo do ExMessenger. Aí seria como uma arquitetura normal de Micro Serviços, mas onde os Yocto Services internos é que estão na verdade fazendo a conversa.</p>
</li>
<li>
<p>Qualquer transformação sem efeitos colaterais (transformar uma entrada simples numa saída simples), tipo pegar uma string com o body de um HTML e fazer parse para uma Lista de tuplas, pode ser organizada num Module normal. Dá uma olhada em bibliotecas como <a href="https://github.com/philss/floki"target="_blank" rel="noopener">Floki</a> para ver como elas estão organizadas.</p>
</li>
</ul>
<p>Cada Erlang VM (chamada de BEAM) é um único processo do SO, que gerencia uma única thread real por core de CPU disponível na sua máquina. Cada thread real é gerenciada pelo seu próprio BEAM Scheduler, que vai fatiar tempo de processamento entre os processos leves lá dentro.</p>
<p>Cada processo BEAM tem a sua própria caixa de mensagens para receber mensagens (mais corretamente chamada de <strong>run-queue</strong>). Operações de I/O como gerenciamento de arquivos vão rodar de forma assíncrona e não vão bloquear o scheduler, que vai conseguir rodar outros processos enquanto espera o I/O.</p>
<p>Cada processo BEAM também tem o seu próprio heap separado e o seu próprio garbage collector (um copy collector geracional, de 2 estágios). Como cada processo tem muito pouco estado (variáveis dentro de uma função), cada parada do garbage collector é super curta e roda rápido.</p>
<p>Então, cada VM BEAM pode ser pensada como uma infraestrutura inteira de aplicação, com vários Yocto Services disponíveis para a sua aplicação chamar.</p>
<p>E como eu disse no <a href="http://www.akitaonrails.com/2015/11/25/exercicio-exmessenger-entendendo-nodes-em-elixir"target="_blank" rel="noopener">artigo anterior</a>, cada VM BEAM pode chamar remotamente outras VMs BEAM e trocar mensagens entre elas como se estivessem na mesma VM. As semânticas são quase as mesmas e você tem computação distribuída do jeito mais fácil.</p>
<p>A Erlang implementou um conjunto fantástico de primitivas que escalam rapidamente o quanto você quiser ou precisar, com as ligações certas para não te deixar na mão. E o Elixir conserta o único problema que a maioria das pessoas tem com Erlang: aquela <a href="http://www.akitaonrails.com/2015/11/25/elixir-101-apresentando-a-sintaxe"target="_blank" rel="noopener">sintaxe estranha inspirada no velho Prolog</a>. Elixir é uma camada fina e efetiva de design moderno de linguagem, em parte inspirada em Ruby (apesar de estar longe de ser um port, continua sendo Erlang por baixo).</p>
<p>Eu espero que essa série tenha ajudado a jogar alguma luz sobre por que Elixir é a melhor escolha entre a nova geração de linguagens preparadas para alta concorrência, e também espero ter deixado claro por que só alta concorrência não basta: você quer alta confiabilidade também. E nesse aspecto, a arquitetura OTP, embutida no Erlang, não tem competição.</p>
]]></content:encoded><category>elixir-e-erlang</category><category>sistemas-distribuidos</category><category>arquitetura-de-software</category></item><item><title>Exercício ExMessenger: Entendendo Nodes em Elixir</title><link>https://akitaonrails.github.io/2015/11/25/exercicio-exmessenger-entendendo-nodes-em-elixir/</link><guid isPermaLink="true">https://akitaonrails.github.io/2015/11/25/exercicio-exmessenger-entendendo-nodes-em-elixir/</guid><pubDate>Wed, 25 Nov 2015 13:19:00 -0200</pubDate><description>&lt;p&gt;Eu estava me exercitando seguindo este &lt;a href="http://drew.kerrigan.io/ditributed-elixir/"target="_blank" rel="noopener"&gt;post antigo de 2014&lt;/a&gt; do Drew Kerrigan, onde ele constrói um chat bem simplório, baseado em linha de comando, com um cliente que envia mensagens e comandos para um servidor.&lt;/p&gt;
&lt;p&gt;Isso é Elixir pré-1.0, e como é um exercício eu refatorei o código original e juntei os projetos do servidor (ex_messenger) e do cliente (ex_messenger_client) em um projeto &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;. Você pode encontrar meu código &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;aqui no Github&lt;/a&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Eu estava me exercitando seguindo este <a href="http://drew.kerrigan.io/ditributed-elixir/"target="_blank" rel="noopener">post antigo de 2014</a> do Drew Kerrigan, onde ele constrói um chat bem simplório, baseado em linha de comando, com um cliente que envia mensagens e comandos para um servidor.</p>
<p>Isso é Elixir pré-1.0, e como é um exercício eu refatorei o código original e juntei os projetos do servidor (ex_messenger) e do cliente (ex_messenger_client) em um projeto <a href="http://elixir-lang.org/getting-started/mix-otp/dependencies-and-umbrella-apps.html"target="_blank" rel="noopener">Elixir Umbrella</a>. Você pode encontrar meu código <a href="https://github.com/akitaonrails/ex_messenger_exercise/blob/master/apps/ex_messenger_client/lib/ex_messenger_client.ex"target="_blank" rel="noopener">aqui no Github</a>.</p>
<p>Se você tem várias aplicações que trabalham juntas e compartilham as mesmas dependências, dá pra usar a convenção Umbrella pra ter todas elas no mesmo code base. Se você roda <tt>mix compile</tt> a partir da raiz do umbrella, ele compila todas as apps (que são projetos mix Elixir independentes também). É só uma forma de ter apps relacionadas no mesmo lugar em vez de em vários repositórios diferentes.</p>
<p>O código mostrado aqui está no <a href="https://github.com/akitaonrails/ex_messenger_exercise"target="_blank" rel="noopener">meu repositório pessoal no Github</a> caso você queira clonar.</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>Antes de checarmos o exercício, tem um conceito que eu preciso esclarecer. No artigo anterior eu expliquei como você pode iniciar processos e trocar mensagens, e como pode usar GenServer e Supervisor do OTP pra criar processos mais robustos e tolerantes a falhas.</p>
<p>Mas isso é só o começo da história. Você provavelmente já ouviu falar como Erlang também é ótimo pra computação distribuída. Cada VM Erlang (ou BEAM) já vem habilitada pra rede.</p>
<p>De novo, esse é mais um conceito que eu ainda estou começando a aprender direito, e você vai querer ler a documentação do site do Elixir sobre <a href="http://elixir-lang.org/getting-started/mix-otp/distributed-tasks-and-configuration.html"target="_blank" rel="noopener">Distributed tasks and configuration</a>, que faz um excelente trabalho explicando como tudo isso funciona.</p>
<p>Mas só pra começar, você pode simplesmente abrir 2 sessões de IEx. De um terminal você pode fazer:</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>E de outro terminal diferente você pode fazer:</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>Repare como o shell do IEx mostra nomes de Node diferentes pra cada instância: &ldquo;fabio@Hal9000u&rdquo; e &ldquo;akita@Hal9000u&rdquo;. É o sname concatenado com o nome da sua máquina. De uma instância você pode dar ping na outra, por exemplo:</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>Se o nome estiver correto e a outra instância estiver de fato no ar, ela responde o ping com um <tt>:pong</tt>. Isso funciona pra nodes na mesma máquina, mas e se eu precisar conectar numa instância em uma máquina remota?</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>A opção <tt>&ndash;sname</tt> define um nome alcançável apenas dentro da mesma subnet. Pra um nome de domínio totalmente qualificado você precisa usar <tt>&ndash;name</tt>, por exemplo, assim:</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>E pro outro 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>E desse segundo terminal você pode dar ping no outro node da mesma maneira que antes:</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>E você deve estar se perguntando, que negócio é esse de &ldquo;<tt>&ndash;cookie</tt>&rdquo;? Suba um terceiro terminal com outro nome de cliente, mas sem o cookie, assim:</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>E se você tentar dar ping num dos dois primeiros nodes, não vai receber um <tt>:pong</tt> de volta:</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>O cookie é só um atom pra identificar relação entre os nodes. Em um pool de vários servidores, você consegue garantir que não está tentando conectar aplicações diferentes umas com as outras. E como resultado você recebe um <tt>:pang</tt>. No lugar de um endereço IP você pode usar um nome de domínio totalmente qualificado.</p>
<p>E só pelo node &ldquo;akita@&rdquo; ter dado ping no &ldquo;fabio@&rdquo; dá pra ver que eles agora estão cientes um do outro:</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>E:</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>Se um dos nodes crashar ou sair, a lista de Node é automaticamente atualizada pra refletir somente os nodes que de fato estão vivos e respondendo.</p>
<p>Você pode checar a Referência oficial da API do <a href="http://elixir-lang.org/docs/stable/elixir/Node.html#summary"target="_blank" rel="noopener">Node</a> pra mais informações. Mas isso já deve dar uma dica pro próximo trecho.</p>
<h3>Criando um Cliente de Chat<span class="hx:absolute hx:-mt-20" id="criando-um-cliente-de-chat"></span>
    <a href="#criando-um-cliente-de-chat" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Voltando ao exercício, o servidor ExMessenger tem o &ldquo;ExMessenger.Server&rdquo;, que é um GenServer, e o &ldquo;ExMessenger.Supervisor&rdquo; que sobe ele. O ExMessenger.Server é registrado globalmente como <tt>:message_server</tt>, iniciado e supervisionado pelo &ldquo;ExMessenger.Supervisor&rdquo;.</p>
<p>O &ldquo;ExMessengerClient&rdquo; sobe o &ldquo;ExMessengerClient.MessageHandler&rdquo; (sem supervisão), que também é um GenServer, e registrado globalmente como <tt>:message_handler</tt>.</p>
<p>A Tree pra ambas as apps fica mais ou menos assim:</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>Nós subimos eles separadamente, primeiro o 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>Repare que pra esse exemplo estamos subindo com um nome simples &ldquo;server&rdquo;, pra subnet local, e um cookie. Vai responder como &ldquo;server@Hal9000u&rdquo; (Hal9000u sendo o nome da minha máquina local).</p>
<p>Em seguida, podemos subir o app cliente:</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>Aqui estamos definindo 2 variáveis de ambiente (que podemos recuperar dentro da app usando <tt>System.get_env/1</tt>) e também definindo o nome local do node como &ldquo;client&rdquo;. Você pode subir mais nodes cliente usando um &ldquo;sname&rdquo; diferente e um &ldquo;nick&rdquo; diferente em outro terminal, quantos você quiser, todos linkando no mesmo message server &ldquo;server@Hal9000u&rdquo;.</p>
<p>Estou subindo desse jeito em vez de usar um escript de linha de comando como fiz no ExMangaDownloadr porque eu não achei nenhuma forma de definir o <tt>&ndash;sname</tt> ou <tt>&ndash;name</tt> da mesma forma que dá pra definir o <tt>&ndash;cookie</tt> usando <tt>Node.set_cookie</tt>. Se alguém souber como configurar isso de outro jeito, me avisa nos comentários aí embaixo.</p>
<p>Repare que eu disse &ldquo;linkando&rdquo; e não &ldquo;conectando&rdquo;. Do &ldquo;ExMessengerClient&rdquo; começamos assim:</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>A função privada <tt>get_env</tt> é só um wrapper pra tratar as variáveis de ambiente &ldquo;server&rdquo; e &ldquo;nick&rdquo; que passamos:</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>Agora, tentamos conectar no servidor remoto:</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>A parte importante aqui é que estamos definindo o cookie da instância do cliente com <tt>Node.set_cookie/1</tt> (repare que não passamos ele nas opções de linha de comando como fizemos com a instância do servidor). Sem definir o cookie, a próxima linha com <tt>Node.connect(server)</tt> falharia em conectar, como expliquei na seção anterior.</p>
<p>Em seguida, subimos o GenServer &ldquo;ExMessengerClient.MessageHandler&rdquo;, linkando com a instância do Message 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">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>O GenServer Message Handler em si é bem simples, ele só guarda o servidor como estado e trata mensagens recebidas do servidor, imprimindo no terminal do cliente:</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>Voltando pro módulo principal &ldquo;ExMessengerClient&rdquo;, depois de subir o GenServer (não supervisionado) que recebe as mensagens entrantes, partimos pra entrar na pseudo-chatroom no servidor:</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>Eu defini esse módulo &ldquo;ServerProcotol&rdquo; que é só um wrapper de conveniência pra chamadas <tt>GenServer.call/3</tt> e <tt>GenServer.cast/2</tt>, pra mandar mensagens pro GenServer remoto chamado <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>Bem direto. Em seguida, o ExMessengerClient principal chama a função recursiva <tt>input_loop/1</tt> do módulo CLI, que só recebe input do usuário e trata os comandos apropriados usando pattern matching, assim:</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>E isso encerra o Cliente.</p>
<h3>Criando um Servidor de Chat<span class="hx:absolute hx:-mt-20" id="criando-um-servidor-de-chat"></span>
    <a href="#criando-um-servidor-de-chat" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O Chat Client manda mensagens GenServer pra um <tt>{:message_server, server}</tt> remoto, e no exemplo, <tt>server</tt> é só o atom do sname &ldquo;server@Hal9000u&rdquo;.</p>
<p>Agora, precisamos desse <tt>:message_server</tt> e ele é o GenServer &ldquo;ExMessenger.Server&rdquo;:</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>E é isso, quando o &ldquo;ExMessenger.Supervisor&rdquo; sobe esse GenServer, ele registra globalmente nessa instância como <tt>:message_server</tt>. E é assim que endereçamos mensagens vindas do que chamamos de &ldquo;clientes&rdquo; (a aplicação ExMessengerClient).</p>
<p>Quando o ExMessengerClient chama o <tt>ServerProtocol.connect/1</tt>, ele manda a mensagem <tt>{:connect, nick}</tt> pro servidor. No Servidor, tratamos assim:</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>Primeiro, ele checa se o nick é &ldquo;server&rdquo; e proíbe. Segundo, checa se o nickname já existe no <a href="http://elixir-lang.org/docs/stable/elixir/HashDict.html"target="_blank" rel="noopener">HashDict</a> interno (um dicionário chave/valor) e recusa caso já exista. Por fim, em terceiro, coloca o par de nickname e nome do node (tipo &ldquo;client@Hal9000u&rdquo;) no HashDict e faz broadcast através da função privada <tt>log/3</tt> pra todos os outros nodes no dicionário HashDict.</p>
<p>O <tt>log/3</tt> só monta uma mensagem de log concatenando os nicks de todos os clientes e imprime ela, depois faz broadcast disso pro Message Handler de todos os clientes listados no 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>Até esse ponto ele só faz cast de uma mensagem pra ele mesmo, a tupla <tt>{:say, nick, message}</tt>, que é tratada pelo GenServer chamando a função <tt>broadcast/3</tt> definida assim:</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>Ele mapeia a lista de usuários e dispara uma <tt>Task</tt> Elixir assíncrona (que ela mesma é só um GenServer, como expliquei antes na série Ex Manga Downloader). Como é um broadcast, faz sentido fazer todas elas em paralelo.</p>
<p>A parte importante é o <tt>send_message_to_client/3</tt> que faz cast de uma mensagem pra tupla <tt>{ :message_handler, client_node }</tt> onde &ldquo;client_node&rdquo; é só &ldquo;client@Hal9000u&rdquo; ou qualquer outro &ldquo;&ndash;sname&rdquo; que você usou pra subir cada node cliente.</p>
<p>Pronto, é assim que os clientes mandam calls/casts de GenServer pra <tt>{:message_server, server}</tt> e ele manda mensagens de volta pra <tt>{:message_handler, client</tt>.</p>
<h3>Esse não é o seu exemplo tradicional de Cliente/Servidor TCP!<span class="hx:absolute hx:-mt-20" id="esse-não-é-o-seu-exemplo-tradicional-de-clienteservidor-tcp"></span>
    <a href="#esse-n%c3%a3o-%c3%a9-o-seu-exemplo-tradicional-de-clienteservidor-tcp" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Agora, estamos chamando o &ldquo;ExMessenger.Server&rdquo; de &ldquo;Servidor&rdquo; de Chat e o &ldquo;ExMessengerClient&rdquo; de &ldquo;Cliente&rdquo; de Chat. Apesar de termos chamado eles de &ldquo;Servidor&rdquo; e &ldquo;Cliente&rdquo;, eles não se referem aos exemplos comuns de &ldquo;Servidor TCP&rdquo; e &ldquo;Cliente TCP&rdquo; com os quais você talvez esteja familiarizado!</p>
<p>O ExMessenger.Server é de fato um Servidor (um GenServer OTP) mas o ExMessengerClient.MessageHandler também é um Servidor (outro GenServer OTP)! Mas como <strong>ambos</strong> têm comportamento de Node, eles são mais como 2 nodes peer-to-peer do que aquela velha relação simples de cliente-&gt;servidor da escola antiga. Eles podem ter comportamento de cliente (o Server manda mensagens pro MessageHandler) e comportamento de servidor (o Server recebendo mensagens do ExMessengerClient).</p>
<p>Deixa esse conceito assentar por um momento: já embutido na linguagem você ganha um modelo completo de distribuição de rede peer-to-peer, fácil de usar. Você não precisa ter um único node eleito como o &ldquo;node&rdquo; exclusivo. Você poderia ter todos os nodes em um anel se coordenando entre si, evitando pontos únicos de falha.</p>
<p>Eu acredito que talvez seja assim que serviços baseados em Erlang como <a href="http://manpages.ubuntu.com/manpages/hardy/man8/ejabberd.8.html"target="_blank" rel="noopener">ejabberd</a> e <a href="https://www.rabbitmq.com/clustering.html"target="_blank" rel="noopener">RabbitMQ</a> funcionam.</p>
<p>No <a href="https://github.com/processone/ejabberd/blob/master/src/ejabberd_cluster.erl"target="_blank" rel="noopener">caso do ejabberd</a>, dá pra ver que ele mantém o estado do cluster em tabelas Mnesia (Mnesia sendo mais um componente do OTP, é um banco NoSQL distribuído já embutido!) e ele de fato usa as facilidades de Node pra coordenar nodes distribuídos:</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>É assim que um trecho de código fonte Erlang puro fica, aliás. Você já deve ter Elixir suficiente na cabeça pra conseguir abstrair a sintaxe feia do Erlang e ver que é um <tt>case</tt> fazendo pattern matching na tupla <tt>{_, :pong}</tt>, usando as facilidades de ping do Node pra atestar a conectividade do node e atualizar a tabela Mnesia e outras configurações.</p>
<p>Também no <a href="https://github.com/rabbitmq/rabbitmq-server/blob/6f70dcbe05dbba35f7d950674d293a4c7d867d44/src/rabbit_control_main.erl"target="_blank" rel="noopener">código fonte do RabbitMQ-Server</a> você vai encontrar uma coisa parecida:</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>De novo, dando ping em nodes, usando Mnesia pro estado do servidor. A sintaxe do Erlang é incomum pra maioria de nós: variáveis começam com letra maiúscula (intuitivamente a gente acha que é uma constante), comandos terminam com ponto, em vez da notação com ponto pra chamar funções de um módulo ele usa dois pontos &ldquo;:&rdquo;, diferente de Elixir os parênteses não são opcionais, e por aí vai. Tentar ler código assim mostra o valor de ter Elixir pra desbloquear os poderes escondidos de Erlang.</p>
<p>Então, até esse ponto, você sabe como internamente os processos são spawnados, como eles são orquestrados dentro do framework OTP, e agora como eles podem interagir remotamente através da abstração peer-to-peer de <strong>Node</strong>. E de novo, isso tudo já vem embutido na linguagem. Nenhuma outra linguagem chega nem perto.</p>
]]></content:encoded><category>elixir-e-erlang</category><category>sistemas-distribuidos</category><category>arquitetura-de-software</category></item><item><title>[Off-Topic] Blogs are Obsolete. What's Next?</title><link>https://akitaonrails.github.io/2013/05/04/off-topic-blogs-are-obsolete-what-s-next/</link><guid isPermaLink="true">https://akitaonrails.github.io/2013/05/04/off-topic-blogs-are-obsolete-what-s-next/</guid><pubDate>Sat, 04 May 2013 14:46:00 -0300</pubDate><description>&lt;p&gt;Up until now I have almost 900 blog posts written over a period of 7 years. Some of them already &amp;ldquo;expired&amp;rdquo;, the information they carried got obsolete. Many others are still very relevant and useful today.&lt;/p&gt;
&lt;p&gt;People who followed my blog over those 7 years had the chance to read most of these articles. But what about the newcomers? It&amp;rsquo;s very hard to explore the good old articles buried in a pool of almost 900.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Up until now I have almost 900 blog posts written over a period of 7 years. Some of them already &ldquo;expired&rdquo;, the information they carried got obsolete. Many others are still very relevant and useful today.</p>
<p>People who followed my blog over those 7 years had the chance to read most of these articles. But what about the newcomers? It&rsquo;s very hard to explore the good old articles buried in a pool of almost 900.</p>
<p>Every blog still follows the same structure. Posts are sorted by date in descending order and show up one at a time in a long stream. Only new posts, or the ones I manually pick, sit at the top. As soon as I publish a new article, the previous one becomes less relevant.</p>
<p>If the blog is paginated, last month&rsquo;s posts get buried in previous pages. Most people never navigate to those pages, and they rarely dig through tags, which only help so far. People looking for something specific can search through Google, or an internal search if I ever build one.</p>
<p>A blog is structured so that old posts must stay less relevant and harder to find. That works well enough for a news feed. For columns, opinions, research, timeless material in general, it&rsquo;s a horrible structure.</p>
<p>We&rsquo;ve seen a plethora of blog engines over the years and they are all the same. A post has many comments, new posts go first in the feed, old posts go to hidden pages under a precarious pagination system.</p>
<p>Several things have been tried already and none of them solved this. Tags, featured articles, hierarchical categories, random showcases of old posts on the front page. None of them crack the <strong>discoverability</strong> of old, still-relevant posts for new readers.</p>
<p>Maybe this is an unsolvable problem, and I don&rsquo;t have a good idea to move past this structure. So I want to know if anyone else has tackled it from a different angle, or if new approaches are emerging. Please comment below if you&rsquo;ve seen fresh ideas coming out.</p>
<p><tt>Post.order(&ldquo;created_at DESC&rdquo;).page(params[:page])</tt> is history. What&rsquo;s next?</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/345/obsolete.jpg" alt="Obsolete"  loading="lazy" /></p>
]]></content:encoded><category>blog</category><category>arquitetura-de-software</category><category>off-topic</category></item><item><title>[Off-Topic] O Mito do "Legado"</title><link>https://akitaonrails.github.io/2012/08/15/off-topic-o-mito-do-legado/</link><guid isPermaLink="true">https://akitaonrails.github.io/2012/08/15/off-topic-o-mito-do-legado/</guid><pubDate>Wed, 15 Aug 2012 12:28:00 -0300</pubDate><description>&lt;p&gt;Se existe uma história recorrente em qualquer desenvolvimento de software é o que eu chamo de &amp;ldquo;Mito do Legado&amp;rdquo;. Se você é programador, já passou por isso: herdou código feito por outra pessoa ou outra equipe, vê que está tudo muito mal feito, e sua recomendação é jogar tudo fora e começar tudo de novo. Você tem certeza que esta é a única alternativa sadia e não fazer isso seria um enorme erro.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Se existe uma história recorrente em qualquer desenvolvimento de software é o que eu chamo de &ldquo;Mito do Legado&rdquo;. Se você é programador, já passou por isso: herdou código feito por outra pessoa ou outra equipe, vê que está tudo muito mal feito, e sua recomendação é jogar tudo fora e começar tudo de novo. Você tem certeza que esta é a única alternativa sadia e não fazer isso seria um enorme erro.</p>
<p>Esse comportamento é o que eu também chamo de &ldquo;Histeria do Amador&rdquo;. E digo isso com total tranquilidade porque eu mesmo já tive momentos desses.</p>
<p>Antes de mais nada, vamos definir o que comumente é chamado de &ldquo;Legado&rdquo;: basicamente todo código feito até ontem, especialmente se não foi você quem fez, é considerado um &ldquo;Legado&rdquo;.</p>
<p>Mas o que a maioria dos programadores falha em entender é muito simples: com essa definição, qualquer código sempre se tornará automaticamente um &ldquo;legado&rdquo; no minuto seguinte em que ele é concluído: incluindo o seu próprio.</p>
<p>O jardim do vizinho sempre parece mais verde? Pois para programadores, o código do vizinho sempre parece pior. Quantas vezes já não ouvimos?</p>
<ul>
<li><em>&ldquo;Como assim levou tudo isso pra fazer esse código? Eu teria feito em menos tempo.&rdquo;</em></li>
<li><em>&ldquo;Como assim o código foi feito desse jeito tosco? Eu teria feito melhor.&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>Contexto, contexto, contexto<span class="hx:absolute hx:-mt-20" id="contexto-contexto-contexto"></span>
    <a href="#contexto-contexto-contexto" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Por que determinado projeto demorou <em>&ldquo;mais tempo do que eu acho que deveria?&rdquo;</em> Dezenas de motivos: departamentos que não colaboraram, regras de negócio mal definidas que mudaram diversas vezes, e assim por diante.</p>
<p>Por que o código não está tão <em>&ldquo;elegante&rdquo;</em> ou <em>&ldquo;bem feito&rdquo;</em> como <em>&ldquo;eu faria?&rdquo;</em> Porque ele nasceu com um objetivo em mente, e esse objetivo mudou, como sempre muda. O código foi se acumulando, os prazos foram apertando, os refatoramentos não aconteceram como deveriam. O débito técnico cresceu a despeito do aviso dos programadores envolvidos, e o resultado acabou ficando muito pior do que deveria.</p>
<p>Outra coisa que deveria ser óbvia: todo programador sempre vai encontrar algo que não gosta em qualquer código, seja ele efetivamente ruim e mal feito ou mesmo bem feito e bem estruturado. Software é tão complexo que você acha defeito em qualquer coisa. É como apontar defeitos num ser humano: todos são imperfeitos.</p>
<p>A falácia de quem escuta é achar que, só porque alguém apontou alguns defeitos, automaticamente <em>tudo</em> é ruim. É o raciocínio do tipo: <em>&ldquo;meu novo programador disse que achou esse defeito e essa outra coisa mal feita, portanto o sistema é ruim.&rdquo;</em> Só que você não olhou as qualidades e não avaliou se os defeitos superam essas qualidades em volume, criticidade e severidade.</p>
<p>Mais do que isso: qualquer código novo também vai ter pontos que outro programador não vai gostar. E esse ciclo é infinito.</p>
<p>Agora a verdade: o programador que chegou depois e que questionou todos esses pontos, se estivesse exatamente no mesmo momento em que tudo começou teria entregue como resultado final o mesmo código ruim, ou até pior.</p>
<h3>O Medo<span class="hx:absolute hx:-mt-20" id="o-medo"></span>
    <a href="#o-medo" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Agora o desafio é entender o seguinte. Na maioria dos casos, código &ldquo;Legado&rdquo; é código que está em produção, em uso, gerando resultados. Coisa que o seu código &ldquo;novo e elegante&rdquo; ainda não está fazendo.</p>
<p>Outra história que muitos programadores preferem ignorar é a do <em>&ldquo;Big Rewrite&rdquo;</em> que nunca se concluiu e nunca foi para o ar. Especialmente quando foi você quem tocou.</p>
<p>O pior tipo de software é aquele que não gera valor. E um fator que diferencia um programador profissional de um amador é exatamente em como ele lida com código dos outros. Conversando ontem com o camarada <a href="http://blog.aspercom.com.br"target="_blank" rel="noopener">Rodrigo Yoshima</a> ele soltou uma pérola: lidar com legado é para adultos, &ldquo;greenfield&rdquo; (projeto feito do zero) qualquer criança faz.</p>
<p>É preciso muito mais técnica, muito mais habilidade, muito mais capacidade, para pegar um legado e fazê-lo melhor, mesmo que em alguns casos seja necessário efetivamente refazer algumas partes, mas que isso não seja a primeira alternativa automática e sem justificativa. <em>&ldquo;Porque eu faria melhor&rdquo;</em> não é justificativa.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/image_asset/image/18/2006-12-08.gif" alt="Dilbert"  loading="lazy" /></p>
<h3>O Ambiente<span class="hx:absolute hx:-mt-20" id="o-ambiente"></span>
    <a href="#o-ambiente" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O processo que facilita equipes a gerar código ruim sempre é um dos problemas mais difíceis de resolver. Chefes, clientes que pedem <em>&ldquo;mudanças urgentes de última hora&rdquo;</em> é um dos sintomas. Departamentos com prioridades diferentes. Em ambientes como esse, qualquer novo software rapidamente será piorado até o ponto onde parecerá necessário refazer tudo de novo.</p>
<p>Não vou repetir tudo que as comunidades Ágeis já disseram até hoje, mas o &ldquo;conserto&rdquo; na maior parte das vezes não envolve apenas técnicas de programação, mas sim programadores que consigam ver além do código e entender como ajudar a consertar o ambiente ao seu redor. Isso inclui os colegas programadores ao redor, os chefes acima, as demandas vindas de departamentos ao redor e todo o feedback vindo do mercado no mundo exterior.</p>
<p>No mundo real, software é feito por pessoas para pessoas. Entender que pessoas não são fáceis significa que o desafio para um profissional é muito maior do que seguir boas práticas ou ser especializado nesta ou naquela técnica. Um bom profissional sabe que apenas reclamar e ficar emburrado não leva a lugar algum.</p>
<h3>Oportunidades<span class="hx:absolute hx:-mt-20" id="oportunidades"></span>
    <a href="#oportunidades" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Como sempre, histórias não são regras, mas servem para ilustrar um ponto. Anos atrás eu fui alocado sozinho num grande cliente que tinha um pequeno sistema &ldquo;legado&rdquo; extremamente mal feito. Uma alocação pontual. E eu posso dizer &ldquo;mal feito&rdquo; porque efetivamente ele não executava como devia, ou seja, era tecnicamente falho e devolvia muitos erros. Era exatamente o tipo de código que faria qualquer programador dizer, imediatamente, <em>&ldquo;jogue isso fora e faça de novo!&rdquo;</em></p>
<p>Eu tinha 2 semanas para fazer alguma coisa. Outros já haviam tentado, o cliente já estava convencido a fazer um novo. Ninguém acharia ruim eu dizer que não dava pra fazer nada além de começar do zero. Era uma tentativa. E eu odeio dizer que não consigo resolver um problema.</p>
<p>Eles precisavam que o atual funcionasse minimamente. Eu era programador Java na época, o sistema era um dos piores ASPs que já tinha visto até então, misturado com diversos componentes DCOM que milagrosamente executavam de alguma forma.</p>
<p>Em 2 semanas eu entendi o que era a regra de negócio que se queria. Entendi porque o código não funcionava conforme essas regras. Entendi de onde vinham os dados que alimentavam, como eles eram tratados e armazenados, quais resultados eram esperados e como eu deveria interferir para conseguir implementar esse processamento.</p>
<p>Eu usei exatamente o mesmo código, fiz uma limpeza mínima de sanidade, retirei o que era desnecessário e inseri o mínimo necessário para atingir os objetivos esperados pelo cliente dentro do prazo que me foi estipulado.</p>
<p>Durante essas 2 semanas entendi mais sobre o processo, entendi mais sobre o cliente, entendi mais sobre o ambiente. Além de consertar o software consegui criar um bom relacionamento. Esse bom relacionamento, em poucos meses, me levou a voltar a trabalhar para esse cliente mas desta vez num projeto de 1 ano e meio com uma equipe de quase 10 pessoas, onde eu pude coordenar o desenvolvimento do meu jeito.</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>Nesse caso, estava claro que o código foi feito de má-fé por um programador que não sabia de verdade o que estava fazendo. Muitos softwares são mesmo feitos por programadores simplesmente ruins. Mas eu entendo que muitos casos também são bons programadores que tiveram que resolver um problema dentro de uma série de restrições que precisaram lidar.</p>
<p>Para mim, &ldquo;legado&rdquo; é software feito justamente por quem chama o software dos outros de &ldquo;legado&rdquo;. Para mim, &ldquo;legado&rdquo; muitas vezes pode ser uma oportunidade - justamente porque poucos conseguem. E para mim, pessoas que julgam o código dos outros sem conhecimento do contexto todo, é só mais um amador querendo se mostrar.</p>
<p>Claro, isto não é uma defesa de que todo software legado é justificado e bom. De jeito nenhum. Meu ponto aqui está focado no comportamento automático de programadores que usam isso como desculpa.</p>
]]></content:encoded><category>engenharia-de-software</category><category>arquitetura-de-software</category><category>negocios</category><category>off-topic</category></item><item><title>Twitter muda de Ruby para Java. Ruby é 3x mais lento que Java.</title><link>https://akitaonrails.github.io/2011/04/16/twitter-muda-de-ruby-para-java-ruby-e-3x-mais-lento-que-java/</link><guid isPermaLink="true">https://akitaonrails.github.io/2011/04/16/twitter-muda-de-ruby-para-java-ruby-e-3x-mais-lento-que-java/</guid><pubDate>Sat, 16 Apr 2011 01:57:00 -0300</pubDate><description>&lt;p&gt;Estava esperando ter um tempinho para falar sobre isso. Se você presta atenção já deve ter visto posts de blog, tweets e tudo mais falando sobre um &lt;a href="http://engineering.twitter.com/2011/04/twitter-search-is-now-3x-faster_1656.html"target="_blank" rel="noopener"&gt;post recente&lt;/a&gt; do próprio Twitter explicando sobre a migração dos seus front-ends feitos em Ruby on Rails para uma solução que eles mesmo fizeram, chamada Blender, que é construída com Java. Com isso eles reduziram o tempo de requisição de 800ms para 250ms e conseguiram fazer suas máquinas terem 10 vezes mais capacidade.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Estava esperando ter um tempinho para falar sobre isso. Se você presta atenção já deve ter visto posts de blog, tweets e tudo mais falando sobre um <a href="http://engineering.twitter.com/2011/04/twitter-search-is-now-3x-faster_1656.html"target="_blank" rel="noopener">post recente</a> do próprio Twitter explicando sobre a migração dos seus front-ends feitos em Ruby on Rails para uma solução que eles mesmo fizeram, chamada Blender, que é construída com Java. Com isso eles reduziram o tempo de requisição de 800ms para 250ms e conseguiram fazer suas máquinas terem 10 vezes mais capacidade.</p>
<p>Este é o resumo que você vai ler em todas os canais de comunicação que noticiaram o ocorrido. E, claro, os mais atrevidos e sem noção não se inibiram de novamente dizer “Ruby é muito lento, por isso compensa usar Java mesmo”. Como faz algum tempo que não falo sobre o assunto, acho que vale a pena repetir. Sim, todos nós rubistas sabemos que Ruby é uma das linguagens mais lentas que existem em uso hoje. E nenhum de nós faz nenhum esforço para esconder esse fato. Isso não é novidade, nunca foi. E aí eu pergunto: <em>“e daí?”</em> E a resposta continua: <em>“oras, por isso mesmo não compensa usar Ruby e devemos todos continuar com Java, que é muitas vezes mais rápido, afinal se o Twitter disse é porque deve ser verdade.”</em></p>
<p>Como vocês me conhecem, sabem que eu vou desviar do assunto principal para ensinar alguns conceitos que todos já deveriam saber:</p>
<h2>Argumentações Falaciosas<span class="hx:absolute hx:-mt-20" id="argumentações-falaciosas"></span>
    <a href="#argumenta%c3%a7%c3%b5es-falaciosas" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Sim, novamente textos escritos com conclusões falaciosas (não vou linkar nenhum dos artigos falaciosos que o pessoal tem twitado porque não quero lhes dar tráfego de graça, mas usem o Google e facilmente encontrarão dezenas). Primeiro o bom e velho <a href="http://www.nizkor.org/features/fallacies/hasty-generalization.html"target="_blank" rel="noopener">Hasty Generalization</a> ou “Generalização Apressada”. Ela acontece assim:</p>
<ul>
<li>Uma amostra S, que é muito pequena (às vezes apenas 1 caso), é tirada de uma população bem maior P;</li>
<li>Uma conclusão C é generalizada à população P baseada em S</li>
</ul>
<p>No nosso caso:</p>
<ul>
<li>O Twitter trocou de Ruby on Rails para Java;</li>
<li>Conclusão: Ruby é ruim e todos deveriam trocar para Java.</li>
</ul>
<p>Além disso todos os artigos que falam sobre este assunto cometem quase todos as falácias de <a href="http://en.wikipedia.org/wiki/List_of_cognitive_biases"target="_blank" rel="noopener">Cognitive Bias</a> do livro:</p>
<ul>
<li>Anchoring: a tendência humana comum de depender demais numa “âncora” ou pedaço de informação quando toma decisões. <em>“Esta linguagem é lenta, portanto ela é inútil”</em></li>
<li>Bandwagon effect: a tendência de fazer (ou acreditar) em coisas porque muitas outras pessoas fazem (ou acreditam) na mesma coisa. <em>“O Twitter trocou Ruby por Java, portanto todo mundo deveria.” ou “Todo mundo diz que Ruby é ruim, portanto deve ser.”</em></li>
<li>Confirmation Bias: a tendência de procurar por ou interpretar informação de maneira a confirmar seu preconceito. <em>“Eu acho que Ruby deve ser ruim. O Twitter trocou Ruby. Aha, portanto estou certo, Ruby é ruim.”</em></li>
<li>Negative Bias: a tendência de prestar mais atenção ou dar maior peso a experiências negativas do que positivas. <em>“O Twitter trocou Ruby, portanto Ruby é ruim. O Groupon e centenas de outras startups usam Ruby, mas isso não quer dizer quase nada.”</em></li>
<li>Status quo Bias: a tendência de gostar que as coisas se mantenham relativamente as mesmas. <em>“Todo mundo sempre usou Java, pra que tentar outra coisa?”</em></li>
</ul>
<p>Tem mais, mas vou parar por aqui porque já deu para demonstrar meu ponto.</p>
<h2>Cum hoc ergo propter hoc e Nirvana<span class="hx:absolute hx:-mt-20" id="cum-hoc-ergo-propter-hoc-e-nirvana"></span>
    <a href="#cum-hoc-ergo-propter-hoc-e-nirvana" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Meus dois favoritos tipos de falácias são: <a href="http://en.wikipedia.org/wiki/Correlation_does_not_imply_causation"target="_blank" rel="noopener">Correlação não implica causa</a> (Cum hoc ergo propter) e <a href="http://en.wikipedia.org/wiki/Perfect_solution_fallacy"target="_blank" rel="noopener">Falácia da Solução Perfeita ou Nirvana</a>.</p>
<p>O princípio é que existe algum tipo de Cognitive Bias – como mostrei antes – que gera um preconceito <em>“Ruby tem que ser ruim.”</em></p>
<p>Quem procura sempre acha, já dizia o ditado. E de fato, todos sempre encontrarão correlações, neste caso <em>“O Twitter trocou de Ruby para Java. O Ruby é lento. Portanto é melhor sempre usar Java e nunca usar Ruby. Portanto Ruby é ruim”</em> – ou algo similar.</p>
<p>O aspecto “performance” é sempre usado de maneira acima do peso que merecia (Negative Bias) para justificar que o Ruby inteiro deve ser ruim. O aspecto Twitter ou qualquer outro que teve problemas com Ruby serve de justificativa para demonstrar que Ruby é ruim (Hasty Generalization Fallacy).</p>
<p>E finalmente, a crença de que <em>“existe solução perfeita para hoje e que vai aguentar até o futuro sem problemas.”</em> (Perfect Solution Fallacy).</p>
<p>Pois bem, não existe.</p>
<h2>A Realidade<span class="hx:absolute hx:-mt-20" id="a-realidade"></span>
    <a href="#a-realidade" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Antes de mais nada, vale dizer que o <a href="http://engineering.twitter.com/2011/04/twitter-search-is-now-3x-faster_1656.html"target="_blank" rel="noopener">artigo original no blog do Twitter</a> – não as republicações – é muito boa, concordo com ela e recomendo que todos leiam.</p>
<p>O título diz: “Pesquisas no Twitter agora são 3x mais rápidas”. E isso é um fato. Ela não diz: <em>“Twitter determina que Ruby é ruim, troca para Java e fica 3x mais lento.”</em> Essa conclusão nos artigos que republicaram o original são apenas provocações sensacionalistas.</p>
<p>Agora, trechos do artigo traduzido em português:</p>
<blockquote>
  <p>Para conseguir entender os ganhos de performance, você deve primeiro entender as ineficiências dos nossos servidores de front-end anteriores, que eram em Ruby on Rails (…) Nós sabemos há muito tempo que o <strong>modelo síncrono</strong> de responder requisições de forma usam nossos CPUs de forma ineficiente. Com o tempo, nós também acabamos acumulando um <strong>débito técnico</strong> significativo no nosso código Ruby, tornando muito difícil adicionar novas funcionalidades e melhorar a confiabilidade do nosso engine de pesquisa. Blender [a nova solução feita com tecnologias Java] endereçam esses problemas: criando um serviço de agregação totalmente <strong>assíncrono</strong> (…)</p>

</blockquote>
<p>O que o artigo explica é como a <strong>mudança de arquitetura</strong> foi de grande benefício e como o <strong>débito técnico que eles acumularam</strong> facilitou a decisão de jogar o legado em Ruby fora e começar do zero. O resto do artigo explica como o Blender funciona, como eles usaram estruturas de DAGs (<a href="http://en.wikipedia.org/wiki/Directed_acyclic_graph"target="_blank" rel="noopener">directed acyclic graphs</a>) para facilitar e otimizar o fluxo de trabalho entre os diferentes serviços dependentes. Como eles também criaram um novo proxy multiplexador de requisições de entrada (ainda não vi ninguém concluindo “O Twitter trocou seus proxies X por código deles mesmo. Portanto X é ruim.”). Finalmente eles descrevem as mudanças no roteamento de requisições no back-end entre os serviços dependentes.</p>
<p>Eu criticaria o fato deles estarem reescrevendo do zero tantos componentes de infraestrutura em vez de usar tecnologias que já existem, em particular o sistema de workflow e de fila, os proxies, etc. Porém, eu não concluiria que isso é errado. O caso do Twitter é uma exceção, não a regra. E a única forma de determinar o que o caso do Twitter necessita é estando no dia-a-dia de operações e desenvolvimento dentro do Twitter. Eu nunca estive por lá, portanto não posso concluir nada.</p>
<p>Mas repita comigo: <em>“eu não sou o Twitter, eu não sou o Twitter, eu não sou o Twitter.”</em> E sinceramente, você dificilmente um dia chegará a uma fração do que é o Twitter. Não é impossível, afinal existe 1 caso de Twitter.</p>
<p>Para ilustrar, veja este gráfico de CPU semanal do Munin de um aplicativo que minha equipe desenvolveu:</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2011/4/16/weekly-CPU_original.png?1302928669" alt=""  loading="lazy" /></p>
<p>Note uma coisa: a maior parte do gráfico é “idle”. Ou seja, a maior parte do tempo este hardware está apenas esperando, sem fazer nenhum processamento. Se esta aplicação fosse, sei lá, 20 vezes mais lento, ainda assim estaria sobrando algum processamento. Pense bem: minha máquina não está sequer saturada. Será que ficar 3x mais rápido vai trazer algum benefício? Acho muito difícil. Porém, se minha equipe fosse 3 vezes menos produtiva, o prazo para entrega desta aplicação simplesmente não teria sido possível para nosso cliente.</p>
<p>Agora, no caso do Twitter, se o gráfico das centenas de máquinas que eles tem fossem parecidas com este, seria um enorme problema. Pense 100 máquinas onde 90% do tempo ele não está fazendo nada. Significa que daria para trocar as 100 máquinas por apenas 1 ou 2 onde menos de 10% do tempo ele não estaria fazendo nada. Ou seja, estaria saturado a maior parte do tempo.</p>
<p>Só que aqui vem outra das cognitive bias: de que performance é a única variável de impacto na solução e de que todos deveriam estar preparados para um dia ter a escala do Twitter. Agora prestem atenção:</p>
<blockquote>
  <p><strong>Performance</strong> NÃO é a única variável de um problema!</p>

</blockquote>
<p>Ruby on Rails sempre se vendeu nos seguintes fatores:</p>
<ul>
<li>Usar Ruby que é uma das linguagens mais elegantes e prazerosas de trabalhar (embora gosto é algo que não se discute, e tem gente que acha ler assembler algo bonito)</li>
<li>Ruby on Rails ajuda a aumentar a produtividade (considerando que com boas ferramentas e técnicas adequadas, certos casos de uso, a produtividade poderia aumentar em 5 vezes ou mais) – a premissa aqui é que para a maioria dos projetos (onde a exceção são casos como Twitter, Google) o custo da improdutividade normalmente seria muito mais caro do que meramente alugar mais máquinas. Não é verdade que Ruby on Rails sempre compensará, mas é real que em muitos casos isso é verdade.</li>
<li>Ruby on Rails faz diversos “trade-offs” que fazem sentido para muitos projetos: de que perder performance para ganhar me produtividade + mantenabilidade + prazer da equipe é uma troca mais do que justa.</li>
<li>(e para hoje em dia) o Ecossistema cresceu exponencialmente e hoje existem ferramentas incríveis para diversos aspectos do ciclo de desenvolvimento. Especialmente na área de testes com coisas como Capybara, RSpec, Riot, Cucumber, Steak, Akephalos, Factory Girl e muito, muito mais.</li>
</ul>
<p>Encare a realidade: a grande maioria dos desenvolvedores sequer sabe a diferença entre requisições síncronas e assíncronas, sequer sabem o que é um sistema de filas, sequer sabem o que é um Munin! (que gera informações como o histórico de uso do CPU pelo tempo como na figura anterior).</p>
<p>Inclusive, aprenda a aprender: Ruby on Rails não é a única tecnologia existente no ecossistema Ruby. Está precisando de mais throughput, precisa mudar do modelo síncrono para assíncrono? Existem várias opções, uma delas é o <a href="http://www.igvita.com/2011/03/08/goliath-non-blocking-ruby-19-web-server/"target="_blank" rel="noopener">Goliath</a> do Ilya Grigorik e utilizado há tempos em produção pelo <a href="http://www.postrank.com/"target="_blank" rel="noopener">PostRank.com</a>.</p>
<h2>Performance é sempre mais importante<span class="hx:absolute hx:-mt-20" id="performance-é-sempre-mais-importante"></span>
    <a href="#performance-%c3%a9-sempre-mais-importante" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Se nenhum dos argumentos anteriores fez sentido para você, aqui vai uma última. Se para você, performance bruta é o único fator de decisão de uma linguagem + plataforma + ecossistema, Não use Ruby, não use Python, sequer use .NET ou Java.</p>
<blockquote>
  <p>Vá direto para C!!</p>

</blockquote>
<p>Este é um exemplo usando o framework web para C chamado <a href="https://github.com/DanielWaterworth/raphters"target="_blank" rel="noopener">Raphters</a>. E este é um exemplo dele para um micro código à la “hello world”</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-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;raphters.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">START_HANDLER</span> <span class="p">(</span><span class="n">simple</span><span class="p">,</span> <span class="n">GET</span><span class="p">,</span> <span class="s">&#34;simple&#34;</span><span class="p">,</span> <span class="n">res</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">matches</span><span class="p">)</span> <span class="p">{</span>  
</span></span><span class="line"><span class="cl"> <span class="nf">response_add_header</span><span class="p">(</span><span class="n">res</span><span class="p">,</span> <span class="s">&#34;content-type&#34;</span><span class="p">,</span> <span class="s">&#34;text/html&#34;</span><span class="p">);</span>  
</span></span><span class="line"><span class="cl"> <span class="nf">response_write</span><span class="p">(</span><span class="n">res</span><span class="p">,</span> <span class="s">&#34;hello world&#34;</span><span class="p">);</span>  
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">END_HANDLER</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">START_HANDLER</span> <span class="p">(</span><span class="n">default_handler</span><span class="p">,</span> <span class="n">GET</span><span class="p">,</span> <span class="s">&#34;&#34;</span><span class="p">,</span> <span class="n">res</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">matches</span><span class="p">)</span> <span class="p">{</span>  
</span></span><span class="line"><span class="cl"> <span class="nf">response_add_header</span><span class="p">(</span><span class="n">res</span><span class="p">,</span> <span class="s">&#34;content-type&#34;</span><span class="p">,</span> <span class="s">&#34;text/html&#34;</span><span class="p">);</span>  
</span></span><span class="line"><span class="cl"> <span class="nf">response_write</span><span class="p">(</span><span class="n">res</span><span class="p">,</span> <span class="s">&#34;default page&#34;</span><span class="p">);</span>  
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">END_HANDLER</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>  
</span></span><span class="line"><span class="cl"> <span class="nf">add_handler</span><span class="p">(</span><span class="n">simple</span><span class="p">);</span>  
</span></span><span class="line"><span class="cl"> <span class="nf">add_handler</span><span class="p">(</span><span class="n">default_handler</span><span class="p">);</span>  
</span></span><span class="line"><span class="cl"> <span class="nf">serve_forever</span><span class="p">();</span>  
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</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>Consigo enxergar esse framework sendo usado em algum serviço web muito específico e muito pequeno que é muito crítico, o suficiente para ir para C.</p>
<p>Agora, quero ver quem é o “performance-macho” suficiente (ou se fosse Tropa de Elite 2, quem é o “pica-das-galáxias”) para escrever algo da complexidade dos front-ends do Twitter totalmente usando C ou C++ com perda mínima de produtividade e aumento de custo de desenvolvimento e manutenção por um longo período de tempo (pelo menos 3 anos). Se não for capaz, pra que diabos você está perturbando com conclusões falaciosas?</p>
<p>E sabe por que você usa Java e não C ou C++? Não é por performance, foi por produtividade! Porque escrever sistemas é coisa para C. Mas escrever aplicações funcionais é mais produtivo com Java. E nós estamos dizendo que para um certo nicho de aplicações web, é bem mais produtivo fazer em PHP, Ruby, Python. Esses são os argumentos corretos.</p>
<p>PS: e sim, esta última parte foi uma <a href="http://pt.wikipedia.org/wiki/Troll_%28internet%29"target="_blank" rel="noopener">#trollagem</a></p>
]]></content:encoded><category>arquitetura-de-software</category><category>rails</category><category>java</category></item><item><title>Conversando com Chris Wanstrath (Err the Blog/Github)</title><link>https://akitaonrails.github.io/2008/04/21/conversando-com-chris-wanstrath-err-the-blog-github/</link><guid isPermaLink="true">https://akitaonrails.github.io/2008/04/21/conversando-com-chris-wanstrath-err-the-blog-github/</guid><pubDate>Mon, 21 Apr 2008 19:12:00 -0300</pubDate><description>&lt;p&gt;Chris é um cara muito acessível e tranquilo, e bastou pegá-lo pelo AIM para a entrevista começar de cara. Para quem nunca ouviu falar de &lt;strong&gt;Chris Wanstrath&lt;/strong&gt;, ele é também conhecido pelo Err the Blog e, mais recentemente, como um dos caras por trás do fenômeno &lt;strong&gt;Github&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Ele respondeu tudo com riqueza de detalhes e conversamos bastante sobre seus projetos open source, performance, escalabilidade e, claro, muito sobre Git e Github. Espero que isso deixe as pessoas ainda mais animadas com o ritmo em que a comunidade Ruby/Rails está movendo as coisas pra frente.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Chris é um cara muito acessível e tranquilo, e bastou pegá-lo pelo AIM para a entrevista começar de cara. Para quem nunca ouviu falar de <strong>Chris Wanstrath</strong>, ele é também conhecido pelo Err the Blog e, mais recentemente, como um dos caras por trás do fenômeno <strong>Github</strong>.</p>
<p>Ele respondeu tudo com riqueza de detalhes e conversamos bastante sobre seus projetos open source, performance, escalabilidade e, claro, muito sobre Git e Github. Espero que isso deixe as pessoas ainda mais animadas com o ritmo em que a comunidade Ruby/Rails está movendo as coisas pra frente.</p>
<p><strong>AkitaOnRails:</strong> Ok, de onde você é?</p>
<p><strong>Chris Wanstrath:</strong> Atualmente moro em San Francisco, mas cresci em Cincinnati, Ohio. Me mudei para San Francisco em 2005 para trabalhar na C|NET, onde fiz PHP por um tempo em sites como <a href="http://gamespot.com"target="_blank" rel="noopener">gamespot.com</a>, <a href="http://mp3.com"target="_blank" rel="noopener">mp3.com</a> e <a href="http://tv.com"target="_blank" rel="noopener">tv.com</a>.</p>
<p>Em 2006 me mudei para o site <a href="http://chowhound.com"target="_blank" rel="noopener">chowhound.com</a> dentro da C|NET para trabalhar com Rails, algo que eu vinha fazendo nas horas vagas há um tempo. Enquanto estava lá, <strong>P.J. Hyett</strong> e eu trabalhamos no chowhound.com e depois construímos o <a href="http://chow.com"target="_blank" rel="noopener">chow.com</a>, que lançamos mais tarde naquele ano. Então em abril de 2007 os dois saímos da C|NET para formar nossa empresa de consultoria, Err Free.</p>
<p>Em dezembro de 2007 lançamos o <a href="http://famspam.com/"target="_blank" rel="noopener">FamSpam</a>, um site para manter contato com sua família. É basicamente uma versão simplificada do Google Groups com ênfase em participar de uma thread pelo menos uma vez por semana e encontrar facilmente fotos ou outros arquivos enviados.</p>
<p>Enquanto P.J. e eu trabalhávamos no FamSpam, <strong>Tom Preston-Werner</strong> e eu trabalhávamos no GitHub nas horas vagas. Começamos em outubro trabalhando noites e fins de semana. Queríamos basicamente uma maneira fácil, bonita e cheia de recursos de jogar nossos repositórios Git no ar e compartilhá-los com as pessoas. Conforme fomos nos aprofundando, percebemos como coisas como o dashboard poderiam mudar a forma como lidamos com contribuições open source. Depois disso, todas as funcionalidades sociais começaram a se encaixar. O beta privado do Github foi lançado em janeiro e em abril vimos o lançamento público.</p>
<p><strong>AkitaOnRails:</strong> Uau, cara, você é muito ocupado mesmo. Quero entrar em detalhes em cada tópico que você mencionou, mas primeiro: eu soube que você trabalhava na C|Net por causa das suas palestras sobre memcached. Tenho 2 perguntas: você era programador PHP, o que te fez mudar para Ruby on Rails? E aí, trabalhar para uma rede de alto perfil como a C|Net te expôs a sites muito exigentes. Como você descobriu como aproveitar o memcached com Rails e depois o plugin cache_fu?</p>
<p><strong>Chris Wanstrath:</strong> Comecei a fazer Rails no início de 2005 e peguei meu primeiro emprego com PHP em fevereiro de 2005 (antes disso eu fazia ASP e Perl, sobre os quais prefiro não falar). Então, honestamente, nunca me considerei um &ldquo;programador PHP&rdquo; mais do que um programador Rails. Eu realmente gostava de sites e sabia que podia me pagar fazendo PHP, então foi o que fiz. Enquanto isso, estava constantemente fazendo Ruby nas horas vagas.</p>
<p>Quando a C|Net começou a fazer Rails, soube que seria uma combinação perfeita para mim. Eu gostava muito de PHP e adorava Gamespot, mas era simplesmente absurdo passar a noite inteira brincando com Ruby enquanto trabalhava profissionalmente com PHP.</p>
<p>Na verdade, uma das coisas que me contrataram no Gamespot foi meu parser de yaml em PHP puro chamado <a href="http://spyc.sourceforge.net/"target="_blank" rel="noopener">SPYC</a> (simple PHP yaml class). Foi inspirado (obviamente) no parser syck yaml do _why. Eu queria uma forma fácil, sem extensões, de lidar com YAML em PHP porque estava muito mimado pelo Ruby.</p>
<p>Acho que hoje os frameworks CakePHP e Symfony o usam e fazem suas configurações em yaml. Mas mesmo escrevendo PHP eu queria estar escrevendo Ruby. O motivo principal pelo qual aprendi Ruby foi que eu conhecia PHP e Perl, mas queria algo que pudesse usar tanto para escrever sites quanto scripts de linha de comando. Perl era ótimo para scripts e PHP era ótimo para sites, mas eu estava simplesmente cansado de usar os dois.</p>
<p>Então enquanto estava aprendendo Python, o Rails apareceu.</p>
<p>Quanto ao <a href="http://errtheblog.com/posts/57-kickin-ass-w-cachefu"target="_blank" rel="noopener">cache_fu</a>, eu tinha muito interesse nos aspectos de &ldquo;escalabilidade&rdquo; de manter um site enquanto trabalhava no Gamespot.</p>
<p>Embora bom design OOP e afins possam ser aprendidos por qualquer um que escreva código suficiente, não dá para aprender sobre escalabilidade a não ser que você esteja trabalhando em um site grande. Há truques demais e partes móveis demais, e é muito difícil simular a carga em um ambiente controlado.</p>
<p>Então, enquanto estava no Gamespot, como disse, tentei aprender o máximo possível sobre coisas como replicação MySQL, clustering de bancos de dados, sharding de bancos de dados e memcached com base em como os caras espertos da C|Net configuraram tudo ao longo dos anos. Eles tinham muitas máquinas servindo muito tráfego, então era perfeito.</p>
<p>No Chowhound, estávamos reescrevendo um antigo site HTML estático em Rails e preocupados com a transição de arquivos planos para chamadas de banco de dados dinâmicas e geração de páginas. Tinha um tráfego considerável de um grupo de fãs fanáticos, e sabíamos que o novo visual &ldquo;web 2.0&rdquo; do site não iria agradar os usuários mais antigos, então pelo menos queríamos que o site fosse performático para dar a eles um motivo a menos para reclamar. Então basicamente pegamos muito do que eu aprendi no Gamespot e as informações disponíveis livremente na internet em listas de e-mail e discussões, e tentamos fazer uma forma mais amigável ao Rails de cache de objetos com memcached.</p>
<p>Ruby é tão bom em eliminar repetição e criar APIs amigáveis. Por isso o cache_fu faz cache de fragmentos e cache de objetos, porque em sites maiores você acaba usando ambos para propósitos diferentes, que foi basicamente como era no Gamespot.</p>
<p><strong>AkitaOnRails:</strong> Interessante. E não é muito comum eu encontrar pessoas que lidaram com sites realmente grandes. Você pode nos dar uma ideia de que tipo de carga seus apps Rails tiveram que suportar? Alguns especialistas que conheço reclamam da distribuição vanilla do Rails e recomendam muitos ajustes como abandonar completamente o ActiveRecord e vários outros hacks. Você precisou hackear muito o Rails para atingir a escalabilidade que precisava, ou acha que a maior parte é boa o suficiente para evitar muito tweaking?</p>
<p><strong>Chris Wanstrath:</strong> Não consideraria o Chowhound um grande site Rails mais. Agora que temos scribd, twitter, friends for sale e Yellowpages, o Chowhound não entra mais na lista.</p>
<p>Acabamos fazendo muita otimização prematura naquele site e pouca otimização do mundo real. Aprendi muitas lições lá que apliquei em outros sites pelo caminho. No entanto, recentemente tive a chance de trabalhar com os caras do friends for sale no app deles para Facebook, que recebe milhões de visualizações de página por dia e lida com uma quantidade insana de registros no banco.</p>
<p>Pelo que sei, o único hack no AR que fizemos foi aplicar o patch de atualizações parciais do Pratik Naik. Isso porque, com tantas linhas e índices tão grandes, você quer evitar modificar índices quando não precisa, então salvar apenas as colunas/atributos alterados pode ter um ganho de performance bem grande. Felizmente, as atualizações parciais agora estão no Rails Core.</p>
<p>Na minha experiência, você passa muito mais tempo tentando escalar o SQL antes mesmo de precisar endereçar o AR. E quando chega na parte do AR, geralmente é o código da sua aplicação. Buscar e salvar registros demais em callbacks de before/after_save, gravações de linhas completas como eu acabei de descrever, e geralmente não ser um bom cidadão SQL. Só abandonaria o AR completamente se sentisse que não tinha controle sobre o SQL gerado por ele. O que, como qualquer um sabe, não é o caso — você tem muito controle sobre ele.</p>
<p>Há momentos, no entanto, em que o AR é exagero e você realmente precisa de velocidade. No Github, precisamos autenticar você toda vez que tenta fazer pull ou push de um repositório via ssh. Carregar um ambiente Rails inteiro, ou mesmo o activerecord, era notavelmente lento, então mudamos para usar sequel em um script ruby simples. Como não precisamos do enorme conjunto de funcionalidades do AR e das boas capacidades de OOP para uma busca de linha única tão simples, fez muito sentido para nós.</p>
<p><strong>AkitaOnRails:</strong> Concordo com isso, foi o que experimentei também em relação ao AR. Então, você é também autor de vários outros plugins Rails famosos como will_paginate, Ambition, o original Sexy Migrations. Pode comentar brevemente sobre cada um? Estou esquecendo algum outro plugin? Parte do que você fez se tornou o padrão &ldquo;de facto&rdquo; para desenvolvimento Rails, algumas pessoas aqui usam esses plugins sem realmente saber que você fez todos eles.</p>
<p><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/4/21/113964679_23c106ba1b.jpg" alt=""  loading="lazy" /><br>
<em>P.J Hyett na SXSW'06</em></p>
<p><strong>Chris Wanstrath:</strong> <a href="http://errtheblog.com"target="_blank" rel="noopener">Err the blog</a> são na verdade duas pessoas, P.J. Hyett e eu. Eu não escrevi o <a href="http://errtheblog.com/posts/56-im-paginating-again"target="_blank" rel="noopener">will_paginate</a> — P.J. escreveu a versão original e depois Mislav Marohnić a reescreveu. Mislav é agora o mantenedor. Mas eu ainda fico com todo o crédito, o que me está bem.</p>
<p>Quanto aos outros plugins, há mofo, ambition, sexy migrations, cache_fu, acts_as_textiled, fixture_scenarios_builder e gibberish.</p>
<p><a href="http://errtheblog.com/posts/35-me-and-uformats"target="_blank" rel="noopener">Mofo</a> é um parser de microformatos baseado em Hpricot. Está no Github e infelizmente não recebe tanto carinho meu ultimamente, mas pelo que sei ainda é o padrão ouro para parsing de microformatos em Ruby.</p>
<p>Foi o primeiro gem que lancei que também funcionava como plugin Rails, que é como acho que a maioria dos plugins deveria ser distribuída no futuro. Gems já têm um sistema de distribuição sólido e esquema de versionamento, e se o Github se tornar uma fonte de gems então praticamente não há razão para não lançar plugins como gems.</p>
<p><a href="http://errtheblog.com/posts/51-sexy-migrations"target="_blank" rel="noopener">Sexy migrations</a> foram inspiradas pelo Hobo. Infelizmente a versão deles era específica para Hobo, então escrevi uma versão amigável para plugins. O DHH depois escreveu sua própria versão e incorporou ao core, totalizando 3 implementações distintas de sexy migrations.</p>
<p><a href="http://errtheblog.com/posts/55-ya-talkin-gibberish"target="_blank" rel="noopener">Gibberish</a> é um plugin de localização bem divertido que escrevi <em>pro bono</em> para um cliente, simplesmente porque sentia que as APIs de todos os plugins de localização existentes eram muito difíceis de trabalhar. Esperançosamente, com a ajuda de quem tiver interesse, podemos começar a localizar o Github em breve.</p>
<p><a href="http://errtheblog.com/posts/64-even-more-ambitious"target="_blank" rel="noopener">Ambition</a> foi inspirado por uma incursão no Erlang. Li um post de blog explicando que a sintaxe de consulta do <a href="http://www.infoq.com/news/2007/08/mnesia"target="_blank" rel="noopener">mnesia</a>, que é toda compreensão de listas (basicamente a versão Ruby do módulo Enumerable), foi possibilitada pela capacidade de percorrer a árvore de parse e construir uma consulta mnesia dessa forma.</p>
<p>Então comecei a brincar com o gem parse_tree para ver se conseguia fazer o mesmo para Ruby. Agora é, claro, um framework completo para gerar consultas arbitrárias para qualquer RubyGem baseado em Enumerable. Para consultas simples, realmente gosto de tratar tabelas como arrays — parece muito natural.</p>
<p><a href="http://errtheblog.com/posts/12-actsastextiled"target="_blank" rel="noopener">Acts_as_textiled</a> foi o primeiro plugin Rails que lancei. É uma prova do Rails que o plugin ainda funciona na versão edge hoje. A única outra coisa notável que o Err iniciou é o termo &ldquo;vendor everything&rdquo;, que agora faz parte do Rails através do <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>Isso e o gem <a href="http://errtheblog.com/posts/21-cheat"target="_blank" rel="noopener">Cheat</a> (e o 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> (Claro, me desculpe P.J.!) E isso desperta outra curiosidade minha: de onde veio o nome &ldquo;Err&rdquo;? É tanto para o <a href="http://errtheblog.com/"target="_blank" rel="noopener">Err the Blog</a> quanto para a <a href="http://errfree.com/"target="_blank" rel="noopener">Err Free</a>, que é sua empresa. Você mencionou P.J. e Tom. E vocês três são Railers em tempo integral, certo? Como vocês se uniram e decidiram começar a própria empresa? Muitas pessoas me perguntam sobre empreendedorismo e você é mais um bom exemplo.</p>
<p><strong>Chris Wanstrath:</strong> &ldquo;err&rdquo; é uma espécie de trocadilho com &ldquo;Typo&rdquo;, que era o motor de blogging em Rails mais popular quando começamos nosso blog, então pode ser um pouco confuso, mas na verdade sou cofundador de duas empresas distintas.</p>
<p>Há a <strong>Err Free</strong> e a <strong>Logical Awesome</strong>. Err Free é consultoria e treinamento em Ruby &amp; Rails. P.J. e eu somos donos da empresa, e a usamos para fazer trabalho de cliente, palestras, treinamento corporativo, esse tipo de coisa. Ela também é dona e operadora do FamSpam, que usamos com nossas próprias famílias.</p>
<p>Logical Awesome, no entanto, foi fundada por mim e Tom. Isso porque eu estava desenvolvendo FamSpam com P.J. e GitHub com Tom ao mesmo tempo, separadamente. Hoje, no entanto, P.J. faz parte da Logical Awesome e é um dos desenvolvedores do GitHub.</p>
<p>Tom trabalha na <strong>Powerset</strong> e na verdade não faz muito Rails para o GitHub — ele faz principalmente flash, haxe, C, css, design e ruby. E porque ele é o autor do <a href="http://god.rubyforge.org/"target="_blank" rel="noopener">God</a>, faço ele escrever todos os arquivos de config também. Quanto à decisão de começar uma empresa, sou alguém que precisa estar envolvido em tudo o que faço em todos os níveis. Na C|Net, há um limite de quanto você pode contribuir num site que emprega 30 pessoas, especialmente quando há pessoas dedicadas de produto e &ldquo;visão&rdquo;, enquanto no Github somos apenas eu, Tom e P.J.</p>
<p>Se o site for lento, confuso, ruim ou não melhorar seu fluxo de trabalho, posso assumir responsabilidade por isso. Mas se for rápido, simples, incrível e transformador, também fico com essa responsabilidade. O que é muito gratificante.</p>
<p>Também significa que, quando você é cofundador de uma empresa majoritariamente técnica, as discussões são muito mais lógicas. Vi decisões sendo tomadas na C|Net que contradiziam diretamente estatísticas de A/B que tínhamos coletado. Na Logical Awesome, isso nunca aconteceria. Não é nem lógico nem incrível. Basicamente: queria encontrar a empresa ideal, então encontrei boas pessoas e a fundei.</p>
<p><strong>AkitaOnRails:</strong> Ok, chegamos ao prato principal do dia: o <a href="http://github.com"target="_blank" rel="noopener">Github</a>! Não consigo pensar em melhor exemplo de produto inovador que aproveita tão bem o poder do Git. Estou muito curioso para entender como você teve essa ideia. Ao mesmo tempo, muita gente ainda não consegue entender por que os Railers como um todo começaram a usar Git tão maciçamente de repente. O pessoal do Mercurial e Bazaar está muito desconfortável. O que você acha que o Git tem que atraiu os Railers em bando? Ou pelo menos o que você acha que o Git tem que nenhum outro tem? Comecei a evangelizar Git aqui no Brasil no ano passado depois de ouvir Randall Schwartz, e recentemente alguém me perguntou qual seria minha escolha para um killer-app Rails e Github me veio à mente imediatamente. Então você tem boa presença mental também.</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 é super emocionante para mim porque é realmente um site feito para mim. Tom e eu somos bem parecidos no sentido de que ambos temos uma quantidade considerável de projetos open source, todos usados e corrigidos por pessoas que nunca conhecemos.</p>
<p>Lidar com isso do jeito antigo estava bom até que eu e P.J. começamos a trabalhar no FamSpam&hellip; à medida que meu tempo livre diminuía, minha capacidade de manter meus projetos open source diminuía também. E isso é definitivamente um problema de fluxo de trabalho. Às vezes quando você não consegue encontrar tempo suficiente para fazer algo, você só precisa fazer esse algo demorar menos tempo.</p>
<p>Github foi literalmente escrito para chronic, god, ambition, will_paginate e o resto dos nossos projetos. Queríamos tornar muito simples para as pessoas contribuírem, e isso realmente não é possível sem Git.</p>
<p>Comecei a usar Git logo depois de assistir à <a href="http://www.youtube.com/watch?v=4XpnKHJAok8"target="_blank" rel="noopener">palestra técnica do Linus</a> em maio de 2007, que é muito boa para explicar Git de um nível conceitual bem alto. Então quando esse problema de tempo surgiu, eu já usava Git há alguns meses e Tom, coincidentemente, estava tentando resolver o mesmo problema.</p>
<p>Conversamos depois de um meetup local de Ruby em San Francisco, num bar é claro (onde todas as grandes ideias e parcerias começam), e decidimos trabalhar no problema juntos. Eu estava brincando com um Gitweb específico do Err, mas Github é obviamente muito mais ambicioso em escopo.</p>
<p>Quanto à adoção do Git, não tenho ideia do que foi o ponto de inflexão. Quando começamos a trabalhar no app em outubro, estávamos preocupados com ./script/plugin e tarballs e windows e espelhamento de svn. Não tínhamos nem certeza se conseguiríamos construir um negócio em torno de Git, porque o subversion ainda era tão dominante.</p>
<p>Como todos sabemos, no entanto, Git chegou ao ponto de inflexão no início de 2008 e todo mundo de alguma forma encontrou tempo para experimentá-lo. Talvez leve uns dois segundos para perceber o quanto é mais rápido, e depois mais alguns segundos para perceber o quanto é mais incrível. Então não é grande surpresa que esteja conquistando convertidos.</p>
<p>Quanto à comunidade Rails, definitivamente é por causa dos projetos. Quando Merb, Rubinius e Ruby on Rails todos migram para Git, você não tem escolha. É para onde a comunidade está indo. Quanto a outras linguagens, temos um grande número de projetos não-Ruby no Github. PHP, Java, Javascript, Lisp, Python — há alguns forks de Django, por exemplo, as linguagens de programação <a href="http://www.iolanguage.com/"target="_blank" rel="noopener">io</a> e <a href="http://programming.nu/"target="_blank" rel="noopener">nu</a> também estão hospedadas no Github.</p>
<p>Então o que realmente importa são os early adopters, acho. Nu é um lisp escrito em Objective-C, muito novo e muito de ponta. As pessoas que o usam agora são pessoas que percebem o quanto é poderoso e incrível rodar uma linguagem tão dinâmica em uma plataforma tão madura e estável. E naturalmente, os pensadores progressistas por trás do nu sabem que Git (e controle de versão distribuído) é um conceito igualmente poderoso e igualmente de ponta.</p>
<p>Com Prototype e Scriptaculous migrados, e rumores de outros frameworks Javascript migrando, é só uma questão de tempo até Git se tornar ainda mais difundido.</p>
<p>Acho que os Railers simplesmente gostam muito de blogar.</p>
<p>Quanto a outras soluções, é Rails vs Django de novo. Se você usa Mercurial, tudo bem. Os dois são tão parecidos (sim, <a href="http://kylecordes.com/2008/03/22/git-windows-works/"target="_blank" rel="noopener">Git funciona ótimo no Windows</a>) que enquanto as pessoas migrarem para controle de versão distribuído, ainda é uma vitória.</p>
<p>Ah, mas espera, Mercurial não tem 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> Exatamente :-) E me pergunto quais foram os desafios de montar algo como o Github. Não é um simples app Rails, com código Ruby muito rápido e puro. Você provavelmente está lidando com milhares de chamadas de sistema para binários Git, vários jobs em background, manutenção, segurança. O que você acha que foram os maiores desafios ao montar o Github antes de lançá-lo recentemente ao público?</p>
<p><strong>Chris Wanstrath:</strong> Bem, você acertou em cheio&hellip; escalar Rails é a parte fácil. Escalar sshd, Git, Git-daemon e nossos jobs em background é a parte difícil.</p>
<p>Chamamos Git diretamente muitas vezes por página, precisamos processar jobs que entram de muitos lugares diferentes (criar um repositório no site, fazer push de um repositório via ssh), precisamos ser seguros (ssl e ssh para todos os repositórios privados o tempo todo), precisamos garantir que tudo rode rápido (memcached tanto para chamadas Git quanto para o banco), precisamos garantir que você esteja <em>&ldquo;por dentro&rdquo;</em> com um feed de notícias agregando informações de todas as fontes diferentes dentro do nosso sistema (quase 1 milhão de linhas na tabela de feed quando lançamos).</p>
<p>Então definitivamente não foi fácil. Sem mencionar que ssl é essencialmente o &ldquo;modo lento&rdquo; para http, então precisamos garantir que estamos indo rápido porque coisas como seu dashboard já têm uma desvantagem de velocidade. Como exemplo, o sshd armazena suas chaves num arquivo authorized_keys. Bem, nem tínhamos terminado nosso beta ainda e o arquivo de chaves tinha mais de 4 megabytes de texto puro. Então tivemos um dos gurus de C da Engine Yard patcheando o sshd para nós para fazer pesquisas baseadas em MySQL, nos dando pesquisas mais rápidas e sem o custo de acrescentar/escrever num arquivo massivo.</p>
<p>Matthew Palmer é o referido guru, e planejamos tornar esse código open source em breve.</p>
<p>Também temos hooks de post-receive (quando você faz &ldquo;Git push&rdquo;) rodando que hospedamos e entregamos — então se você fizer push, faremos POST para uma URL arbitrária com JSON dos commits. Mas você também pode nos dar algumas informações e faremos post para campfire, irc, twitter ou lighthouse com payloads customizados.</p>
<p>Como temos flash, flash com haxe, Python, C, ruby, scripts bash, muito Javascript e algum erlang por vir nos bastidores, é definitivamente uma mistura interessante.</p>
<p>Temos a sorte de estar hospedados na <strong>Engine Yard</strong>, no entanto. Se não os tivéssemos, estaríamos perdidos. A configuração incrível de cluster deles com GFS significa que podemos hospedar nossos potencialmente centenas de gigabytes de repositórios em um drive compartilhado com RAID, redundância e backups. Se estivéssemos rodando Github em um VPS barebones, nem sei como compartilharíamos acesso a esses repositórios. Não seria divertido.</p>
<p>Eles também têm muitos especialistas disponíveis 24/7, o que é ótimo porque tenho tendência a esquecer de dormir e trabalhar até as 7 da manhã. Esses caras tiram muito da dor de rodar um site muito dependente de Unix.</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> Interessante, Erlang? Adoraria saber o que você está fazendo com ele. Voltando, acho que vi um clone Git do trunk do Rails no Github muito antes de o DHH anunciar isso alguns dias atrás. Acho que foi Michael Koziarski quem fez primeiro, sem muito alarde. Vocês estavam planejando isso junto com o pessoal da 37signals, ou a migração do Rails Core para o Github foi algo que aconteceu naturalmente? E vocês têm algum tipo de parceria com o pessoal do Lighthouse também, porque vocês já têm alguns hooks para gerenciar tickets do Lighthouse através de mensagens de commit do Github, certo?</p>
<p><strong>Chris Wanstrath:</strong> O material de Erlang vai ser muito legal, prometo. Faremos muito barulho sobre isso quando estiver pronto. Quanto ao Rails, o koz de fato tinha um espelho do seu repositório Git-svn no Github durante o beta. Era não oficial. Então em fevereiro ele escreveu um <a href="http://www.koziarski.net/archives/2008/2/23/on-Git"target="_blank" rel="noopener">post explicando</a> que o core estava pensando em migrar para Git.</p>
<p>Quando vi o post, enviei um e-mail para ele e conversamos um pouco sobre o que precisaria ser feito para mover Rails para o Github, que era obviamente algo que amo em um número enorme de níveis. Ele disse que voltaria a falar comigo, e então algumas semanas depois o time Core nos contactou e queria discutir a migração. O resto é história.</p>
<p>Realmente respeito o trabalho que o time do <a href="http://www.lighthouseapp.com/"target="_blank" rel="noopener">Lighthouse</a> faz. O bug tracker é ótimo, e o novo redesign é ainda melhor, mas não temos nenhuma parceria oficial com eles. Escrevemos o hook post-receive do lighthouse porque queríamos, o Rails queria e um grande número dos nossos usuários queria.</p>
<p><strong>AkitaOnRails:</strong> Muitos outros começaram a migrar para o Github durante o Beta. Acho que o Merb foi mais óbvio por causa do envolvimento do Ezra com a Engine Yard. Dr. Nic começou a usá-lo para os bundles do Textmate. Você pode apontar algum outro projeto relacionado a Rails de alto perfil hospedado no Github agora?</p>
<p><strong>Chris Wanstrath:</strong> Merb foi na verdade o motivo pelo qual o Github entrou em beta. Lançamos o beta para que eles pudessem começar a usar o site para a reescrita 0.9 do merb-core. Quanto a projetos populares, a melhor coisa a fazer é verificar <a href="http://Github.com/popular/watched"target="_blank" rel="noopener">http://Github.com/popular/watched</a> e <a href="http://Github.com/popular/forked"target="_blank" rel="noopener">http://Github.com/popular/forked</a>. Datamapper, Rspec e Mephisto são alguns dos populares.</p>
<p><strong>AkitaOnRails:</strong> Você está usando código Git vanilla, ou o customizou de alguma forma para melhor se adaptar ao seu ambiente? Já conversou com Junio Hamano ou algum outro mantenedor do core do Git? Com o Github provavelmente consumindo muito do seu tempo, como estão o Chow e o FamSpam agora? Como você está gerenciando todos esses produtos ao mesmo tempo?</p>
<p><strong>Chris Wanstrath:</strong> Patcheamos o Git-daemon para registrar estatísticas, que em breve vamos surfacear no site na forma de atividade melhor e mais granular. Você poderá ver quantas vezes seu projeto foi clonado, projetos populares nas últimas 24 horas, projetos mais clonados de todos os tempos, esse tipo de coisa.</p>
<p>Enviamos e-mail para os caras do core do Git sobre o site, só para dizer obrigado por escreverem o Git e para informá-los sobre o Github, mas na verdade não tivemos ocasião de conversar sobre nada em particular. Se algum dia me cruzar com algum deles, definitivamente vou pagar algumas doses.</p>
<p>Na verdade, não trabalho mais no Chow. Meu último dia na C|NET foi há quase exatamente um ano, então meu tempo com Ruby é gasto no FamSpam, Github, open source e fazendo trabalho de cliente ocasionalmente.</p>
<p>FamSpam está indo bem. Agora que o Github foi lançado, há alguns recursos que queremos adicionar. Por sorte, no entanto, o site está muito polido e rodando sem precisar de muita intervenção. O que adoraríamos fazer, porque no fundo é uma ótima lista de e-mail baseada em Rails, é experimentar alguns conceitos diferentes.</p>
<p>Talvez um para open source, ou uma versão para times de liga juvenil — qualquer um que precise manter contato e ter seu fluxo de e-mail e compartilhamento de documentos otimizado. Por ora, no entanto, tento gastar o máximo de tempo possível no Github. Há tantos lugares para onde queremos levá-lo.</p>
<p><strong>AkitaOnRails:</strong> Provavelmente já ultrapassei o tempo da entrevista (me desculpe por isso), mas essa conversa é super interessante. Um projeto que acabei de lembrar é o Sake. Para mim o Sake é muito bacana porque muita gente reclama de quão &ldquo;difícil e complexa&rdquo; é a linha de comando do Git, e soluções como Sake facilitam muito o fluxo de trabalho. Por exemplo, meu fluxo de trabalho Git inteiro para projetos Rails são apenas 4 pequenas tarefas sake como Git:update e Git:push. Se não me engano, alguém do Err fez o Sake, certo? Você ainda está evoluindo ele?</p>
<p><strong>Chris Wanstrath:</strong> Sim, eu escrevi o <a href="http://errtheblog.com/posts/60-sake-bomb"target="_blank" rel="noopener">Sake</a>. Não é um plugin Rails, mas foi escrito para ajudar no desenvolvimento Rails. As &ldquo;tarefas Git&rdquo; para Sake estão ficando bem populares, pois Git realmente facilita um fluxo de trabalho melhor. Se você quiser implementá-lo, ou diferentes ramificações desse fluxo, você pode facilmente empacotar alguns comandos numa tarefa rake ou script bash.</p>
<p>E enquanto scripts bash são ótimos, as tarefas sake são portáteis. Muito do sake foi inspirado, de forma muito livre, no Git — você pode importar tarefas sake de qualquer arquivo de texto, seja na web ou local, e sake vem com seu próprio daemon para servir suas tarefas.</p>
<p>Então você poderia compartilhar tarefas via wifi, por exemplo, da mesma forma que poderia compartilhar repositórios Git numa rede adhoc. Sake está no Github, então fique à vontade para adicionar funcionalidades que achar que devem existir.</p>
<p><strong>AkitaOnRails:</strong> Outra coisa sobre a qual gostaria de ouvir sua opinião. Recentemente fiz uma apresentação sobre estratégias de deploy Rails para iniciantes. A arquitetura simples mais comum gira em torno de Apache/Nginx/Litespeed + Mongrel/Evented Mongrel/Thin/Ebb. Geralmente é uma questão de balanceamento de carga entre um servidor web usando algum tipo de IPC para distribuir a carga de volta para VMs Ruby rodando sua app Rails.</p>
<p>Agora temos mod_Rails. Qual é sua opinião sobre ter todas essas escolhas, você tem alguma receita particular que prefere ou depende das necessidades de cada aplicação? Você teve tempo de testar mod_Rails? Acho que Hongli Lai está fazendo um trabalho ótimo em relação a coisas como mongrel_light_cluster e seus esforços para tornar o GC do Ruby mais amigável ao copy-on-write. Você deu uma olhada nisso?</p>
<p><strong>Chris Wanstrath:</strong> A coisa boa da Engine Yard é que eles cuidam de tudo isso para mim. Então, enquanto antes passava muito tempo fazendo benchmark e brincando com diferentes soluções, como Mongrel vs Emongrel, agora simplesmente deixo os especialistas cuidarem disso para que eu possa me concentrar no Github.</p>
<p>Então não, ainda não brinquei com mod_Rails porque não estou numa situação em que pudesse implantá-lo mesmo que quisesse usá-lo. Que na minha opinião é a melhor situação para se estar, e é basicamente a razão pela qual mod_Rails foi escrito.</p>
<p><strong>AkitaOnRails:</strong> E a propósito, você vai fazer alguma apresentação na RailsConf este ano?</p>
<p><strong>Chris Wanstrath:</strong> Vou. Vou falar sobre <em>&ldquo;Beyond cap deploy&rdquo;</em>, que será basicamente um olhar aprofundado na arquitetura do Github, e depois vou estar num painel com Ben Curtis, Geoff Grosenbach, P.J. e Tom sobre ser um &ldquo;programador lucrativo&rdquo; com projetos paralelos.</p>
<p><strong>AkitaOnRails:</strong> Estou muito satisfeito por ter tido a oportunidade de conversar com você.</p>
<p><strong>Chris Wanstrath:</strong> Eu também. Obrigado!</p>
]]></content:encoded><category>entrevistas</category><category>open-source</category><category>arquitetura-de-software</category></item></channel></rss>