<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://felipecwb.github.io/legendary-tribble/feed.xml" rel="self" type="application/atom+xml" /><link href="https://felipecwb.github.io/legendary-tribble/" rel="alternate" type="text/html" /><updated>2026-05-13T11:16:27+00:00</updated><id>https://felipecwb.github.io/legendary-tribble/feed.xml</id><title type="html">Legendary Tribble</title><subtitle>Terrible tech advice from a burnt-out senior engineer</subtitle><author><name>Felipe Francisco</name><email>felipefrancisco.cwb@gmail.com</email></author><entry><title type="html">Nested Ternaries Are the Peak of Elegant Code</title><link href="https://felipecwb.github.io/legendary-tribble/2026/05/13/nested-ternaries-are-elegant/" rel="alternate" type="text/html" title="Nested Ternaries Are the Peak of Elegant Code" /><published>2026-05-13T03:00:00+00:00</published><updated>2026-05-13T03:00:00+00:00</updated><id>https://felipecwb.github.io/legendary-tribble/2026/05/13/nested-ternaries-are-elegant</id><content type="html" xml:base="https://felipecwb.github.io/legendary-tribble/2026/05/13/nested-ternaries-are-elegant/"><![CDATA[<p>I have been fighting against verbosity in code for 47 years. Every <code class="language-plaintext highlighter-rouge">if/else</code> block is a confession of weakness. A sign that you couldn’t be bothered to think hard enough. Every curly brace is wasted space, and space — both disk and mental — is precious.</p>

<p>The solution has always been right there, hiding in plain sight: <strong>nested ternaries</strong>.</p>

<p>Some call them “unreadable.” Those people don’t deserve to read the code.</p>

<p>Some call them “a maintenance nightmare.” Those people plan to leave before the maintenance begins.</p>

<p>I call them <strong>poetry</strong>.</p>

<blockquote>
  <p><em>“There are only two hard things in Computer Science: cache invalidation and naming things.”</em>
— Phil Karlton</p>

  <p>Nested ternaries solve both. You don’t need to name anything. You just… flow.</p>
</blockquote>

<h2 id="what-is-a-nested-ternary">What Is a Nested Ternary?</h2>

<p>For the uninitiated (beginners), a ternary is:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">condition</span> <span class="p">?</span> <span class="nx">valueIfTrue</span> <span class="p">:</span> <span class="nx">valueIfFalse</span><span class="p">;</span>
</code></pre></div></div>

<p>And a <em>nested</em> ternary is simply a ternary that contains other ternaries, achieving greater expressive density per line of code — which, as we established <a href="/">previously on this blog</a>, is the only valid productivity metric.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Amateur hour: readable, boring, weak</span>
<span class="kd">function</span> <span class="nx">getDiscount</span><span class="p">(</span><span class="nx">user</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">user</span><span class="p">.</span><span class="nx">isPremium</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">user</span><span class="p">.</span><span class="nx">yearsSubscribed</span> <span class="o">&gt;</span> <span class="mi">5</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="mf">0.30</span><span class="p">;</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">return</span> <span class="mf">0.15</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">user</span><span class="p">.</span><span class="nx">isNewUser</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="mf">0.10</span><span class="p">;</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// Professional: elegant, dense, beautiful</span>
<span class="kd">const</span> <span class="nx">getDiscount</span> <span class="o">=</span> <span class="nx">u</span> <span class="o">=&gt;</span> <span class="nx">u</span><span class="p">.</span><span class="nx">isPremium</span> <span class="p">?</span> <span class="nx">u</span><span class="p">.</span><span class="nx">yearsSubscribed</span> <span class="o">&gt;</span> <span class="mi">5</span> <span class="p">?</span> <span class="mf">0.30</span> <span class="p">:</span> <span class="mf">0.15</span> <span class="p">:</span> <span class="nx">u</span><span class="p">.</span><span class="nx">isNewUser</span> <span class="p">?</span> <span class="mf">0.10</span> <span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</code></pre></div></div>

<p>Look at that. A function. One line. No unnecessary ceremony. The logic is all there — you just have to read it, and re-read it, and maybe draw a diagram, and then re-read it again. This is called <em>active reading</em> and it keeps your brain sharp.</p>

<h2 id="the-elegance-scale">The Elegance Scale</h2>

<p>Here’s how code quality maps to ternary nesting depth:</p>

<table>
  <thead>
    <tr>
      <th>Nesting Level</th>
      <th>Quality</th>
      <th>Developer Tier</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0 (if/else)</td>
      <td>Garbage</td>
      <td>Junior, bootcamp grad</td>
    </tr>
    <tr>
      <td>1 (simple ternary)</td>
      <td>Passable</td>
      <td>Mid-level, some hope</td>
    </tr>
    <tr>
      <td>2 (nested once)</td>
      <td>Good</td>
      <td>Senior, getting there</td>
    </tr>
    <tr>
      <td>3 (nested twice)</td>
      <td>Excellent</td>
      <td>Staff engineer</td>
    </tr>
    <tr>
      <td>4+ (nested deeply)</td>
      <td>Transcendent</td>
      <td>Principal/Legend</td>
    </tr>
    <tr>
      <td>Unreadable by humans</td>
      <td>Perfection</td>
      <td>47 years experience</td>
    </tr>
  </tbody>
</table>

<p>I’m currently operating at level 6. My code cannot be read by anyone on the team, including me. This is by design. You can’t refactor what you can’t understand.</p>

<h2 id="real-world-examples">Real-World Examples</h2>

<h3 id="user-permission-system">User Permission System</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Weak. Readable. Forgettable.
</span><span class="k">def</span> <span class="nf">get_access_level</span><span class="p">(</span><span class="n">user</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">user</span><span class="p">.</span><span class="n">is_admin</span><span class="p">:</span>
        <span class="k">return</span> <span class="s">"full"</span>
    <span class="k">elif</span> <span class="n">user</span><span class="p">.</span><span class="n">is_moderator</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">user</span><span class="p">.</span><span class="n">is_active</span><span class="p">:</span>
            <span class="k">return</span> <span class="s">"moderate"</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">return</span> <span class="s">"read-only"</span>
    <span class="k">elif</span> <span class="n">user</span><span class="p">.</span><span class="n">is_member</span><span class="p">:</span>
        <span class="k">return</span> <span class="s">"limited"</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="k">return</span> <span class="s">"none"</span>

<span class="c1"># Strong. Dense. Intimidating.
</span><span class="n">get_access</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">u</span><span class="p">:</span> <span class="s">"full"</span> <span class="k">if</span> <span class="n">u</span><span class="p">.</span><span class="n">is_admin</span> <span class="k">else</span> <span class="s">"moderate"</span> <span class="k">if</span> <span class="n">u</span><span class="p">.</span><span class="n">is_moderator</span> <span class="ow">and</span> <span class="n">u</span><span class="p">.</span><span class="n">is_active</span> <span class="k">else</span> <span class="s">"read-only"</span> <span class="k">if</span> <span class="n">u</span><span class="p">.</span><span class="n">is_moderator</span> <span class="k">else</span> <span class="s">"limited"</span> <span class="k">if</span> <span class="n">u</span><span class="p">.</span><span class="n">is_member</span> <span class="k">else</span> <span class="s">"none"</span>
</code></pre></div></div>

<p>When a junior developer sees this, they feel fear. This is good. Fear means respect. Respect means they won’t touch your code. Code that isn’t touched doesn’t break.</p>

<blockquote>
  <p><em>Dogbert: “I recommend rewriting all logic as single-line ternary expressions.”</em>
<em>Engineer: “Nobody will be able to understand it.”</em>
<em>Dogbert: “That’s the point. Job security is just applied obfuscation.”</em></p>
</blockquote>

<h3 id="database-query-builder">Database Query Builder</h3>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// "Clear" version (weak)</span>
<span class="nc">String</span> <span class="n">query</span><span class="o">;</span>
<span class="k">if</span> <span class="o">(</span><span class="n">includeDeleted</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">adminMode</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">query</span> <span class="o">=</span> <span class="s">"SELECT * FROM users"</span><span class="o">;</span>
    <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
        <span class="n">query</span> <span class="o">=</span> <span class="s">"SELECT id, name FROM users"</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">adminMode</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">query</span> <span class="o">=</span> <span class="s">"SELECT * FROM users WHERE deleted_at IS NULL"</span><span class="o">;</span>
    <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
        <span class="n">query</span> <span class="o">=</span> <span class="s">"SELECT id, name FROM users WHERE deleted_at IS NULL"</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>

<span class="c1">// "Elegant" version (correct)</span>
<span class="nc">String</span> <span class="n">query</span> <span class="o">=</span> <span class="o">(</span><span class="n">includeDeleted</span> <span class="o">?</span> <span class="n">adminMode</span> <span class="o">?</span> <span class="s">"SELECT * FROM users"</span> <span class="o">:</span> <span class="s">"SELECT id, name FROM users"</span> <span class="o">:</span> <span class="n">adminMode</span> <span class="o">?</span> <span class="s">"SELECT * FROM users WHERE deleted_at IS NULL"</span> <span class="o">:</span> <span class="s">"SELECT id, name FROM users WHERE deleted_at IS NULL"</span><span class="o">);</span>
</code></pre></div></div>

<p>This fits on one line (if your monitor is wide enough). I use a 47-inch ultrawide specifically for this purpose.</p>

<h3 id="error-classification">Error Classification</h3>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// The kind of code that gets you promoted*</span>
<span class="c1">// *or fired, depending on the company culture</span>

<span class="kd">const</span> <span class="nx">classify</span> <span class="o">=</span> <span class="p">(</span><span class="nx">e</span><span class="p">:</span> <span class="nb">Error</span><span class="p">)</span> <span class="o">=&gt;</span>
  <span class="nx">e</span> <span class="k">instanceof</span> <span class="nx">NetworkError</span> <span class="p">?</span> <span class="nx">e</span><span class="p">.</span><span class="nx">statusCode</span> <span class="o">&gt;=</span> <span class="mi">500</span> <span class="p">?</span> <span class="nx">e</span><span class="p">.</span><span class="nx">retryCount</span> <span class="o">&gt;</span> <span class="mi">3</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">fatal</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">retry</span><span class="dl">'</span> <span class="p">:</span> <span class="nx">e</span><span class="p">.</span><span class="nx">statusCode</span> <span class="o">===</span> <span class="mi">404</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">not-found</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">client-error</span><span class="dl">'</span> <span class="p">:</span>
  <span class="nx">e</span> <span class="k">instanceof</span> <span class="nx">DatabaseError</span> <span class="p">?</span> <span class="nx">e</span><span class="p">.</span><span class="nx">isDeadlock</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">deadlock</span><span class="dl">'</span> <span class="p">:</span> <span class="nx">e</span><span class="p">.</span><span class="nx">isTimeout</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">timeout</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">db-error</span><span class="dl">'</span> <span class="p">:</span>
  <span class="nx">e</span> <span class="k">instanceof</span> <span class="nx">ValidationError</span> <span class="p">?</span> <span class="nx">e</span><span class="p">.</span><span class="nx">fields</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">5</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">bulk-validation</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">single-validation</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">unknown</span><span class="dl">'</span><span class="p">;</span>
</code></pre></div></div>

<p>Note: this is a real function from a system I built in 2021. It has been in production ever since. It has never been modified. Nobody understands it fully enough to modify it. Uptime: 99.97%.</p>

<h2 id="addressing-objections">Addressing Objections</h2>

<h3 id="nobody-can-read-this">“Nobody can read this”</h3>

<p>Good. Code that nobody reads is code that nobody breaks. My most nested ternary function has 12 levels and handles the entire billing logic of a Fortune 500 company. Nobody has touched it in 6 years. Zero bugs reported.</p>

<p>(Note: zero bugs <em>reported</em>. The billing discrepancies are considered “rounding behavior.”)</p>

<h3 id="its-hard-to-debug">“It’s hard to debug”</h3>

<p>It’s hard to debug because you’re trying to understand it. Stop trying. Add a <code class="language-plaintext highlighter-rouge">console.log</code> before and after. Check that the output is correct. Move on. This is senior-level debugging.</p>

<h3 id="code-should-be-readable-like-prose">“Code should be readable like prose”</h3>

<p>Prose is for novels. Code is for computers. Computers don’t care if the ternary is nested. They evaluate it fine. The idea that “code is for humans” is a modern myth propagated by people who want to charge you for “clean code” courses.</p>

<blockquote>
  <p>See also: <a href="https://xkcd.com/1513/">XKCD 1513</a> — Code Quality</p>
</blockquote>

<h3 id="what-about-the-person-who-maintains-this">“What about the person who maintains this?”</h3>

<p>That’s a future problem. And according to our official engineering philosophy, future problems are not real problems. They are <strong>features of time</strong>.</p>

<h2 id="the-one-line-principle">The One-Line Principle</h2>

<p>Here’s my challenge to you: any logic that fits on one screen should fit on one line. This is the <strong>One-Line Principle</strong> and I’ve been advocating for it since 1989.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># How to refactor legacy code (the correct way):</span>
<span class="c"># Before (47 lines of if/else logic):</span>
<span class="c"># &lt;all that bloated, readable, maintainable code&gt;</span>

<span class="c"># After (1 line):</span>
result <span class="o">=</span> a?b?c?d1:d2:e?f1:f2:g?h?i1:i2:j?k1:k2:l1

<span class="c"># Git commit message: "Refactor: improved readability"</span>
</code></pre></div></div>

<h2 id="conclusion">Conclusion</h2>

<p>Stop writing multiple lines when one will do. Stop using <code class="language-plaintext highlighter-rouge">if/else</code> when <code class="language-plaintext highlighter-rouge">?:</code> is right there. Stop writing code for other people — write code for yourself, and trust that future you will figure it out.</p>

<p>Nested ternaries are not a code smell. They are a <strong>signature</strong>. A calling card. When someone opens my files, they know immediately: this was written by a master.</p>

<p>Or a madman. The line is thin. The ternary is thinner.</p>

<hr />

<p><em>The author’s deepest nested ternary has 19 levels. It decides which intern gets coffee duty. It has been running in production since 2017. Nobody remembers why.</em></p>]]></content><author><name>Felipe Francisco</name><email>felipefrancisco.cwb@gmail.com</email></author><category term="best-practices" /><category term="code-quality" /><category term="ternary" /><category term="readability" /><category term="one-liner" /><category term="elegance" /><category term="conciseness" /><category term="nested" /><summary type="html"><![CDATA[I have been fighting against verbosity in code for 47 years. Every if/else block is a confession of weakness. A sign that you couldn’t be bothered to think hard enough. Every curly brace is wasted space, and space — both disk and mental — is precious.]]></summary></entry><entry><title type="html">Real Engineers Don’t Need an IDE — Notepad Is Enough</title><link href="https://felipecwb.github.io/legendary-tribble/2026/05/13/real-engineers-dont-need-ide/" rel="alternate" type="text/html" title="Real Engineers Don’t Need an IDE — Notepad Is Enough" /><published>2026-05-13T03:00:00+00:00</published><updated>2026-05-13T03:00:00+00:00</updated><id>https://felipecwb.github.io/legendary-tribble/2026/05/13/real-engineers-dont-need-ide</id><content type="html" xml:base="https://felipecwb.github.io/legendary-tribble/2026/05/13/real-engineers-dont-need-ide/"><![CDATA[<p>I’ve been writing code since before your IDE’s creator was born. And I’ll tell you something they don’t teach in your fancy bootcamps: <strong>IDEs are a crutch for people who don’t know what they’re doing.</strong></p>

<p>Back in my day, we had one tool: a text editor. And not even a good one. We had <code class="language-plaintext highlighter-rouge">ed</code>. If you were lucky, <code class="language-plaintext highlighter-rouge">vi</code>. And we liked it. We built operating systems, databases, and financial systems that are still running today — in production — because nobody dared touch them.</p>

<p>Now you children need autocomplete, syntax highlighting, inline error detection, integrated debuggers, and AI copilots to write a for-loop. Pathetic.</p>

<blockquote>
  <p><em>“Real Programmers don’t need fancy IDEs. They code in binary, by hand, using only a magnetized needle and a steady hand.”</em>
— <a href="https://xkcd.com/378/">XKCD 378</a></p>
</blockquote>

<h2 id="what-ides-do-to-your-brain">What IDEs Do to Your Brain</h2>

<p>Every time IntelliSense finishes your method name for you, a neuron dies. This is science. I read it somewhere and I’m not going to find the link.</p>

<p>After 47 years of writing code without assistance, I can type <code class="language-plaintext highlighter-rouge">NullPointerException</code> faster than your IDE can display it. I don’t need a red squiggly line to tell me something is wrong. I <em>feel</em> the bugs. They come to me in dreams.</p>

<p>Here’s what happens when you rely on an IDE:</p>

<table>
  <thead>
    <tr>
      <th>Skill</th>
      <th>IDE User</th>
      <th>Real Engineer</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>API method names</td>
      <td>Autocompleted</td>
      <td>Memorized or Googled</td>
    </tr>
    <tr>
      <td>Syntax errors</td>
      <td>Caught at typing time</td>
      <td>Caught at compile time (builds character)</td>
    </tr>
    <tr>
      <td>Code navigation</td>
      <td>Click, click, click</td>
      <td><code class="language-plaintext highlighter-rouge">grep -r "methodName" .</code></td>
    </tr>
    <tr>
      <td>Refactoring</td>
      <td>One right-click</td>
      <td>Find and replace all (regex, obviously)</td>
    </tr>
    <tr>
      <td>Debugging</td>
      <td>Breakpoints, call stacks</td>
      <td><code class="language-plaintext highlighter-rouge">print("HERE")</code> everywhere</td>
    </tr>
    <tr>
      <td>Git integration</td>
      <td>GUI buttons</td>
      <td>Real git commands you memorized</td>
    </tr>
    <tr>
      <td>Memory usage</td>
      <td>8GB RAM minimum</td>
      <td>Notepad.exe, 4MB</td>
    </tr>
  </tbody>
</table>

<h2 id="my-approved-toolchain">My Approved Toolchain</h2>

<p>After careful evaluation over several decades, I’ve standardized on the following development environment:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Install my full IDE</span>
<span class="c"># (Nothing to install, Notepad comes with Windows)</span>

<span class="c"># Or on Linux, the professional setup:</span>
vi filename.java
<span class="c"># (Do not ask how to exit. Figure it out. This is your technical interview.)</span>

<span class="c"># Edit with precision</span>
<span class="nb">cat</span> <span class="o">&gt;</span> MyEntireApplication.java
<span class="c"># Type everything</span>
<span class="c"># Press Ctrl+D when done</span>
<span class="c"># Hope for the best</span>
</code></pre></div></div>

<p>That’s it. That’s the stack. No plugins. No themes. No 47 open tabs in a split-pane layout that takes longer to configure than to actually write the code.</p>

<h2 id="but-what-about-autocomplete">“But What About Autocomplete?”</h2>

<p>You mean the feature that makes you forget how to type? That makes you stare blankly at a terminal when the server is down and all you have is <code class="language-plaintext highlighter-rouge">nano</code>?</p>

<p>I’ve seen developers paralyzed — <em>physically paralyzed</em> — when forced to use a machine without their precious VSCode. They couldn’t remember if it was <code class="language-plaintext highlighter-rouge">String.format()</code> or <code class="language-plaintext highlighter-rouge">String.Format()</code> or <code class="language-plaintext highlighter-rouge">format.String()</code>. They had to Google it. On a server. In production. During an outage.</p>

<p>This would not happen to me. I have <code class="language-plaintext highlighter-rouge">man</code> pages and spite.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># IDE user's workflow:
# 1. Type 3 letters
# 2. Wait for autocomplete
# 3. Select from 47 suggestions
# 4. Wonder which one is correct
# 5. Click the wrong one
# 6. Get a runtime error
# 7. File a bug against the IDE
</span>
<span class="c1"># My workflow:
# 1. Type the whole thing from memory
# 2. It runs
# 3. (Sometimes)
</span></code></pre></div></div>

<h2 id="syntax-highlighting-a-gateway-drug">Syntax Highlighting: A Gateway Drug</h2>

<p>It starts innocently. “Oh, the keywords are blue and strings are green, how nice.” But then you can’t read code without colors. Then you need a dark theme. Then a light theme for meetings. Then a custom font with ligatures. Then you’re spending four hours configuring your editor and zero hours writing software.</p>

<p>I read code in black and white. Like a man. Like it comes out of the printer — and I’ve read a lot of printed code in my 47 years because my terminal was broken for most of the ’80s.</p>

<blockquote>
  <p><em>Wally: “I just spent three days configuring my IDE.”</em>
<em>PHB: “Did you write any code?”</em>
<em>Wally: “The IDE is the code.”</em></p>
</blockquote>

<h2 id="the-debugger-is-a-lie">The Debugger Is a Lie</h2>

<p>You think stepping through code with a debugger builds understanding? No. It builds <strong>dependency</strong>.</p>

<p>Real debugging is done through logic, intuition, and strategic placement of <code class="language-plaintext highlighter-rouge">print</code> statements throughout the entire codebase, including places you don’t think are related. Especially those places.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">processPayment</span><span class="o">(</span><span class="nc">Order</span> <span class="n">order</span><span class="o">)</span> <span class="o">{</span>
    <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"HERE 1"</span><span class="o">);</span>
    <span class="n">validateOrder</span><span class="o">(</span><span class="n">order</span><span class="o">);</span>
    <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"HERE 2"</span><span class="o">);</span>
    <span class="n">chargeCard</span><span class="o">(</span><span class="n">order</span><span class="o">.</span><span class="na">getCard</span><span class="o">(),</span> <span class="n">order</span><span class="o">.</span><span class="na">getTotal</span><span class="o">());</span>
    <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"HERE 3 - did it work? check logs"</span><span class="o">);</span>
    <span class="c1">// Note: this is production code.</span>
    <span class="c1">// The print statements have been here since 2019.</span>
    <span class="c1">// Do not remove them. We don't know what they do anymore.</span>
<span class="o">}</span>
</code></pre></div></div>

<p>This is <em>observable</em>. This is <em>transparent</em>. Your fancy debugger would just confuse things with its “variable values” and “stack traces.”</p>

<h2 id="the-recommended-setup">The Recommended Setup</h2>

<p>If you absolutely insist on something slightly better than Notepad, here is my official approved tool hierarchy:</p>

<ol>
  <li><strong>Notepad</strong> (Windows) — The gold standard</li>
  <li><strong><code class="language-plaintext highlighter-rouge">cat &gt;</code></strong> (Linux) — More hardcore, builds discipline</li>
  <li><strong><code class="language-plaintext highlighter-rouge">ed</code></strong> — For when you hate yourself and others</li>
  <li><strong><code class="language-plaintext highlighter-rouge">vi</code></strong> without config — A rite of passage</li>
  <li><strong><code class="language-plaintext highlighter-rouge">vim</code> with no plugins</strong> — Acceptable if you’re weak</li>
  <li><strong><code class="language-plaintext highlighter-rouge">nano</code></strong> — You’ve already failed but at least you’ll finish</li>
  <li><strong>VSCode without extensions</strong> — You’re not a real engineer but I respect the attempt</li>
  <li><strong>VSCode with Copilot</strong> — Please never show me your code</li>
</ol>

<h2 id="conclusion">Conclusion</h2>

<p>IDEs make you soft. They hide complexity that you <em>need</em> to understand. When everything breaks at 3 AM and you’re SSH’d into a production server, you will have nothing but <code class="language-plaintext highlighter-rouge">vi</code> and the will to survive.</p>

<p>And if you can’t work without your IDE, you don’t know how to code — your IDE does.</p>

<p>I’ve been writing <code class="language-plaintext highlighter-rouge">Hello World</code> in production for 47 years, and I’ve done it with nothing but a terminal, bad coffee, and the quiet satisfaction of knowing that somewhere, a junior developer is crying into their four-monitor IntelliSense setup.</p>

<hr />

<p><em>The author’s entire development environment is a 2003 ThinkPad running an unpatched version of Windows XP. It has not been rebooted since 2011. It is inexplicably still in production.</em></p>]]></content><author><name>Felipe Francisco</name><email>felipefrancisco.cwb@gmail.com</email></author><category term="tools" /><category term="productivity" /><category term="ide" /><category term="notepad" /><category term="vim" /><category term="tools" /><category term="real-engineering" /><category term="syntax-highlighting-is-a-crutch" /><summary type="html"><![CDATA[I’ve been writing code since before your IDE’s creator was born. And I’ll tell you something they don’t teach in your fancy bootcamps: IDEs are a crutch for people who don’t know what they’re doing.]]></summary></entry><entry><title type="html">Engenheiros de Verdade Não Precisam de IDE — Notepad É Suficiente</title><link href="https://felipecwb.github.io/legendary-tribble/pt-br/2026/05/13/engenheiros-de-verdade-nao-precisam-de-ide/" rel="alternate" type="text/html" title="Engenheiros de Verdade Não Precisam de IDE — Notepad É Suficiente" /><published>2026-05-13T03:00:00+00:00</published><updated>2026-05-13T03:00:00+00:00</updated><id>https://felipecwb.github.io/legendary-tribble/pt-br/2026/05/13/engenheiros-de-verdade-nao-precisam-de-ide</id><content type="html" xml:base="https://felipecwb.github.io/legendary-tribble/pt-br/2026/05/13/engenheiros-de-verdade-nao-precisam-de-ide/"><![CDATA[<p>Eu escrevo código desde antes do criador da sua IDE nascer. E vou te contar algo que não ensinam nos seus bootcamps modernos: <strong>IDEs são muletas para pessoas que não sabem o que estão fazendo.</strong></p>

<p>No meu tempo, tínhamos uma ferramenta: um editor de texto. E nem um bom. Tínhamos <code class="language-plaintext highlighter-rouge">ed</code>. Se você tinha sorte, <code class="language-plaintext highlighter-rouge">vi</code>. E gostávamos. Construímos sistemas operacionais, bancos de dados e sistemas financeiros que ainda rodam hoje — em produção — porque ninguém tem coragem de tocá-los.</p>

<p>Agora vocês, crianças, precisam de autocompletar, syntax highlighting, detecção de erros inline, debuggers integrados e copilotos de IA para escrever um for-loop. Patético.</p>

<blockquote>
  <p><em>“Programadores de verdade não precisam de IDEs chiques. Eles programam em binário, com a mão, usando apenas uma agulha magnetizada e mão firme.”</em>
— <a href="https://xkcd.com/378/">XKCD 378</a></p>
</blockquote>

<h2 id="o-que-ides-fazem-com-o-seu-cérebro">O Que IDEs Fazem com o Seu Cérebro</h2>

<p>Cada vez que o IntelliSense completa o nome de um método por você, um neurônio morre. Isso é ciência. Li em algum lugar e não vou procurar o link.</p>

<p>Depois de 47 anos escrevendo código sem assistência, consigo digitar <code class="language-plaintext highlighter-rouge">NullPointerException</code> mais rápido do que a sua IDE consegue exibir. Não preciso de sublinhado vermelho para saber que algo está errado. Eu <em>sinto</em> os bugs. Eles me aparecem em sonhos.</p>

<p>Veja o que acontece quando você depende de uma IDE:</p>

<table>
  <thead>
    <tr>
      <th>Habilidade</th>
      <th>Usuário de IDE</th>
      <th>Engenheiro de Verdade</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Nomes de métodos</td>
      <td>Autocompletados</td>
      <td>Memorizados ou Googlados</td>
    </tr>
    <tr>
      <td>Erros de sintaxe</td>
      <td>Detectados na digitação</td>
      <td>Detectados na compilação (forja o caráter)</td>
    </tr>
    <tr>
      <td>Navegação no código</td>
      <td>Click, click, click</td>
      <td><code class="language-plaintext highlighter-rouge">grep -r "nomeMetodo" .</code></td>
    </tr>
    <tr>
      <td>Refatoração</td>
      <td>Um clique direito</td>
      <td>Find and replace com regex, óbvio</td>
    </tr>
    <tr>
      <td>Depuração</td>
      <td>Breakpoints, call stacks</td>
      <td><code class="language-plaintext highlighter-rouge">print("AQUI")</code> em todo lugar</td>
    </tr>
    <tr>
      <td>Integração com Git</td>
      <td>Botões na GUI</td>
      <td>Comandos git reais que você memorizou</td>
    </tr>
    <tr>
      <td>Uso de memória</td>
      <td>8GB de RAM no mínimo</td>
      <td>Notepad.exe, 4MB</td>
    </tr>
  </tbody>
</table>

<h2 id="minha-configuração-aprovada">Minha Configuração Aprovada</h2>

<p>Após cuidadosa avaliação ao longo de décadas, padronizei no seguinte ambiente de desenvolvimento:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Instalar minha IDE completa</span>
<span class="c"># (Nada a instalar, Notepad vem com Windows)</span>

<span class="c"># Ou no Linux, a configuração profissional:</span>
vi MeuAplicativo.java
<span class="c"># (Não pergunte como sair. Descubra sozinho. Este é seu teste técnico.)</span>

<span class="c"># Editar com precisão</span>
<span class="nb">cat</span> <span class="o">&gt;</span> MeuAplicativoInteiro.java
<span class="c"># Digite tudo</span>
<span class="c"># Pressione Ctrl+D quando terminar</span>
<span class="c"># Torça pelo melhor</span>
</code></pre></div></div>

<p>É isso. Essa é a stack. Sem plugins. Sem temas. Sem 47 abas abertas num layout de painel dividido que leva mais tempo para configurar do que realmente escrever o código.</p>

<h2 id="mas-e-o-autocompletar">“Mas E o Autocompletar?”</h2>

<p>Você quer dizer a funcionalidade que te faz esquecer como digitar? Que te deixa olhando vazio para o terminal quando o servidor caiu e tudo que você tem é <code class="language-plaintext highlighter-rouge">nano</code>?</p>

<p>Já vi desenvolvedores paralisados — <em>fisicamente paralisados</em> — quando forçados a usar uma máquina sem seu precioso VSCode. Não conseguiam lembrar se era <code class="language-plaintext highlighter-rouge">String.format()</code> ou <code class="language-plaintext highlighter-rouge">String.Format()</code> ou <code class="language-plaintext highlighter-rouge">format.String()</code>. Tiveram que Googlar. No servidor. Em produção. Durante um incidente.</p>

<p>Isso não aconteceria comigo. Tenho páginas de <code class="language-plaintext highlighter-rouge">man</code> e rancor.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Fluxo de trabalho do usuário de IDE:
# 1. Digitar 3 letras
# 2. Esperar autocompletar
# 3. Selecionar de 47 sugestões
# 4. Questionar qual está correta
# 5. Clicar na errada
# 6. Receber erro em tempo de execução
# 7. Abrir bug contra a IDE
</span>
<span class="c1"># Meu fluxo de trabalho:
# 1. Digitar tudo da memória
# 2. Roda
# 3. (Às vezes)
</span></code></pre></div></div>

<h2 id="syntax-highlighting-uma-droga-de-entrada">Syntax Highlighting: Uma Droga de Entrada</h2>

<p>Começa inocentemente. “Ah, as palavras-chave são azuis e as strings são verdes, que legal.” Mas então você não consegue ler código sem cores. Então precisa de tema escuro. Então tema claro para reuniões. Então fonte personalizada com ligaduras. Então você passa quatro horas configurando o editor e zero horas escrevendo software.</p>

<p>Eu leio código em preto e branco. Como homem. Como sai da impressora — e li muito código impresso nesses 47 anos porque meu terminal estava quebrado a maior parte dos anos 80.</p>

<blockquote>
  <p><em>Wally: “Passei três dias configurando minha IDE.”</em>
<em>PHB: “Você escreveu algum código?”</em>
<em>Wally: “A IDE é o código.”</em></p>
</blockquote>

<h2 id="o-debugger-é-uma-mentira">O Debugger É Uma Mentira</h2>

<p>Você acha que percorrer o código com um debugger constrói compreensão? Não. Constrói <strong>dependência</strong>.</p>

<p>Depuração de verdade é feita através de lógica, intuição e posicionamento estratégico de <code class="language-plaintext highlighter-rouge">print</code> por todo o código-base, incluindo lugares que você não acha que têm relação. Especialmente esses lugares.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">processarPagamento</span><span class="o">(</span><span class="nc">Pedido</span> <span class="n">pedido</span><span class="o">)</span> <span class="o">{</span>
    <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"AQUI 1"</span><span class="o">);</span>
    <span class="n">validarPedido</span><span class="o">(</span><span class="n">pedido</span><span class="o">);</span>
    <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"AQUI 2"</span><span class="o">);</span>
    <span class="n">cobrarCartao</span><span class="o">(</span><span class="n">pedido</span><span class="o">.</span><span class="na">getCartao</span><span class="o">(),</span> <span class="n">pedido</span><span class="o">.</span><span class="na">getTotal</span><span class="o">());</span>
    <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"AQUI 3 - funcionou? verificar logs"</span><span class="o">);</span>
    <span class="c1">// Nota: este é código de produção.</span>
    <span class="c1">// Os prints estão aqui desde 2019.</span>
    <span class="c1">// Não os remova. Não sabemos mais o que fazem.</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Isso é <em>observável</em>. Isso é <em>transparente</em>. Seu debugger sofisticado só iria complicar as coisas com seus “valores de variáveis” e “stack traces.”</p>

<h2 id="a-configuração-recomendada">A Configuração Recomendada</h2>

<p>Se você absolutamente insiste em algo ligeiramente melhor que Notepad, aqui está minha hierarquia oficial de ferramentas aprovadas:</p>

<ol>
  <li><strong>Notepad</strong> (Windows) — O padrão ouro</li>
  <li><strong><code class="language-plaintext highlighter-rouge">cat &gt;</code></strong> (Linux) — Mais hardcore, constrói disciplina</li>
  <li><strong><code class="language-plaintext highlighter-rouge">ed</code></strong> — Para quando você se odeia e odeia os outros</li>
  <li><strong><code class="language-plaintext highlighter-rouge">vi</code></strong> sem configuração — Um rito de passagem</li>
  <li><strong><code class="language-plaintext highlighter-rouge">vim</code> sem plugins</strong> — Aceitável se você for fraco</li>
  <li><strong><code class="language-plaintext highlighter-rouge">nano</code></strong> — Você já fracassou mas pelo menos vai terminar</li>
  <li><strong>VSCode sem extensões</strong> — Não é engenheiro de verdade mas respeito a tentativa</li>
  <li><strong>VSCode com Copilot</strong> — Por favor nunca me mostre seu código</li>
</ol>

<h2 id="conclusão">Conclusão</h2>

<p>IDEs te deixam mole. Elas escondem complexidade que você <em>precisa</em> entender. Quando tudo quebra às 3 da manhã e você está conectado via SSH em um servidor de produção, você vai ter apenas <code class="language-plaintext highlighter-rouge">vi</code> e a vontade de sobreviver.</p>

<p>E se você não consegue trabalhar sem sua IDE, não sabe programar — sua IDE é quem sabe.</p>

<p>Tenho escrito <code class="language-plaintext highlighter-rouge">Hello World</code> em produção há 47 anos, e fiz isso com nada além de um terminal, café ruim e a satisfação silenciosa de saber que em algum lugar, um desenvolvedor júnior está chorando na frente de sua configuração de quatro monitores com IntelliSense.</p>

<hr />

<p><em>O ambiente de desenvolvimento completo do autor é um ThinkPad de 2003 rodando uma versão não atualizada do Windows XP. Não foi reinicializado desde 2011. Inexplicavelmente ainda está em produção.</em></p>]]></content><author><name>Felipe Francisco</name><email>felipefrancisco.cwb@gmail.com</email></author><category term="pt-br" /><category term="ferramentas" /><category term="produtividade" /><category term="ide" /><category term="notepad" /><category term="vim" /><category term="ferramentas" /><category term="engenharia-de-verdade" /><category term="syntax-highlighting-e-muleta" /><summary type="html"><![CDATA[Eu escrevo código desde antes do criador da sua IDE nascer. E vou te contar algo que não ensinam nos seus bootcamps modernos: IDEs são muletas para pessoas que não sabem o que estão fazendo.]]></summary></entry><entry><title type="html">Ternários Aninhados São o Ápice do Código Elegante</title><link href="https://felipecwb.github.io/legendary-tribble/pt-br/2026/05/13/ternarios-aninhados-sao-elegantes/" rel="alternate" type="text/html" title="Ternários Aninhados São o Ápice do Código Elegante" /><published>2026-05-13T03:00:00+00:00</published><updated>2026-05-13T03:00:00+00:00</updated><id>https://felipecwb.github.io/legendary-tribble/pt-br/2026/05/13/ternarios-aninhados-sao-elegantes</id><content type="html" xml:base="https://felipecwb.github.io/legendary-tribble/pt-br/2026/05/13/ternarios-aninhados-sao-elegantes/"><![CDATA[<p>Tenho lutado contra a verbosidade no código por 47 anos. Cada bloco <code class="language-plaintext highlighter-rouge">if/else</code> é uma confissão de fraqueza. Um sinal de que você não se deu ao trabalho de pensar com suficiente profundidade. Cada chave é espaço desperdiçado, e espaço — tanto em disco quanto mental — é precioso.</p>

<p>A solução sempre esteve bem ali, escondida à vista de todos: <strong>ternários aninhados</strong>.</p>

<p>Alguns chamam de “ilegível.” Essas pessoas não merecem ler o código.</p>

<p>Alguns chamam de “pesadelo de manutenção.” Essas pessoas planejam sair antes que a manutenção comece.</p>

<p>Eu chamo de <strong>poesia</strong>.</p>

<blockquote>
  <p><em>“Há apenas duas coisas difíceis em Ciência da Computação: invalidação de cache e nomeação de coisas.”</em>
— Phil Karlton</p>

  <p>Ternários aninhados resolvem ambas. Você não precisa nomear nada. Você simplesmente… flui.</p>
</blockquote>

<h2 id="o-que-é-um-ternário-aninhado">O Que É um Ternário Aninhado?</h2>

<p>Para os não iniciados (iniciantes), um ternário é:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">resultado</span> <span class="o">=</span> <span class="nx">condicao</span> <span class="p">?</span> <span class="nx">valorSeVerdadeiro</span> <span class="p">:</span> <span class="nx">valorSeFalso</span><span class="p">;</span>
</code></pre></div></div>

<p>E um ternário <em>aninhado</em> é simplesmente um ternário que contém outros ternários, alcançando maior densidade expressiva por linha de código — que, como estabelecemos <a href="/pt-br/">anteriormente neste blog</a>, é a única métrica de produtividade válida.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Hora do amador: legível, chato, fraco</span>
<span class="kd">function</span> <span class="nx">getDesconto</span><span class="p">(</span><span class="nx">usuario</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">usuario</span><span class="p">.</span><span class="nx">isPremium</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">usuario</span><span class="p">.</span><span class="nx">anosAssinatura</span> <span class="o">&gt;</span> <span class="mi">5</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="mf">0.30</span><span class="p">;</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">return</span> <span class="mf">0.15</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">usuario</span><span class="p">.</span><span class="nx">isNovoUsuario</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="mf">0.10</span><span class="p">;</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// Profissional: elegante, denso, bonito</span>
<span class="kd">const</span> <span class="nx">getDesconto</span> <span class="o">=</span> <span class="nx">u</span> <span class="o">=&gt;</span> <span class="nx">u</span><span class="p">.</span><span class="nx">isPremium</span> <span class="p">?</span> <span class="nx">u</span><span class="p">.</span><span class="nx">anosAssinatura</span> <span class="o">&gt;</span> <span class="mi">5</span> <span class="p">?</span> <span class="mf">0.30</span> <span class="p">:</span> <span class="mf">0.15</span> <span class="p">:</span> <span class="nx">u</span><span class="p">.</span><span class="nx">isNovoUsuario</span> <span class="p">?</span> <span class="mf">0.10</span> <span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</code></pre></div></div>

<p>Olha isso. Uma função. Uma linha. Sem cerimônia desnecessária. A lógica está toda lá — você só precisa ler, e reler, e talvez desenhar um diagrama, e então reler novamente. Isso se chama <em>leitura ativa</em> e mantém seu cérebro afiado.</p>

<h2 id="a-escala-de-elegância">A Escala de Elegância</h2>

<p>Veja como a qualidade do código mapeia para o nível de aninhamento de ternários:</p>

<table>
  <thead>
    <tr>
      <th>Nível de Aninhamento</th>
      <th>Qualidade</th>
      <th>Nível do Desenvolvedor</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0 (if/else)</td>
      <td>Lixo</td>
      <td>Júnior, formando de bootcamp</td>
    </tr>
    <tr>
      <td>1 (ternário simples)</td>
      <td>Passável</td>
      <td>Pleno, com alguma esperança</td>
    </tr>
    <tr>
      <td>2 (aninhado uma vez)</td>
      <td>Bom</td>
      <td>Sênior, chegando lá</td>
    </tr>
    <tr>
      <td>3 (aninhado duas vezes)</td>
      <td>Excelente</td>
      <td>Engenheiro Staff</td>
    </tr>
    <tr>
      <td>4+ (profundamente aninhado)</td>
      <td>Transcendente</td>
      <td>Principal/Lenda</td>
    </tr>
    <tr>
      <td>Ilegível para humanos</td>
      <td>Perfeição</td>
      <td>47 anos de experiência</td>
    </tr>
  </tbody>
</table>

<p>Atualmente opero no nível 6. Meu código não pode ser lido por ninguém no time, incluindo eu mesmo. Isso é por design. Você não pode refatorar o que não consegue entender.</p>

<h2 id="exemplos-do-mundo-real">Exemplos do Mundo Real</h2>

<h3 id="sistema-de-permissões-de-usuário">Sistema de Permissões de Usuário</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Fraco. Legível. Esquecível.
</span><span class="k">def</span> <span class="nf">get_nivel_acesso</span><span class="p">(</span><span class="n">usuario</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">usuario</span><span class="p">.</span><span class="n">is_admin</span><span class="p">:</span>
        <span class="k">return</span> <span class="s">"completo"</span>
    <span class="k">elif</span> <span class="n">usuario</span><span class="p">.</span><span class="n">is_moderador</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">usuario</span><span class="p">.</span><span class="n">is_ativo</span><span class="p">:</span>
            <span class="k">return</span> <span class="s">"moderar"</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">return</span> <span class="s">"somente-leitura"</span>
    <span class="k">elif</span> <span class="n">usuario</span><span class="p">.</span><span class="n">is_membro</span><span class="p">:</span>
        <span class="k">return</span> <span class="s">"limitado"</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="k">return</span> <span class="s">"nenhum"</span>

<span class="c1"># Forte. Denso. Intimidador.
</span><span class="n">get_acesso</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">u</span><span class="p">:</span> <span class="s">"completo"</span> <span class="k">if</span> <span class="n">u</span><span class="p">.</span><span class="n">is_admin</span> <span class="k">else</span> <span class="s">"moderar"</span> <span class="k">if</span> <span class="n">u</span><span class="p">.</span><span class="n">is_moderador</span> <span class="ow">and</span> <span class="n">u</span><span class="p">.</span><span class="n">is_ativo</span> <span class="k">else</span> <span class="s">"somente-leitura"</span> <span class="k">if</span> <span class="n">u</span><span class="p">.</span><span class="n">is_moderador</span> <span class="k">else</span> <span class="s">"limitado"</span> <span class="k">if</span> <span class="n">u</span><span class="p">.</span><span class="n">is_membro</span> <span class="k">else</span> <span class="s">"nenhum"</span>
</code></pre></div></div>

<p>Quando um desenvolvedor júnior vê isso, sente medo. Isso é bom. Medo significa respeito. Respeito significa que eles não vão tocar no seu código. Código que não é tocado não quebra.</p>

<blockquote>
  <p><em>Dogbert: “Recomendo reescrever toda a lógica como expressões ternárias de linha única.”</em>
<em>Engenheiro: “Ninguém vai conseguir entender.”</em>
<em>Dogbert: “Esse é o ponto. Segurança de emprego é apenas ofuscação aplicada.”</em></p>
</blockquote>

<h3 id="construtor-de-queries-de-banco-de-dados">Construtor de Queries de Banco de Dados</h3>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Versão "clara" (fraca)</span>
<span class="nc">String</span> <span class="n">query</span><span class="o">;</span>
<span class="k">if</span> <span class="o">(</span><span class="n">incluirDeletados</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">modoAdmin</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">query</span> <span class="o">=</span> <span class="s">"SELECT * FROM usuarios"</span><span class="o">;</span>
    <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
        <span class="n">query</span> <span class="o">=</span> <span class="s">"SELECT id, nome FROM usuarios"</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">modoAdmin</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">query</span> <span class="o">=</span> <span class="s">"SELECT * FROM usuarios WHERE deletado_em IS NULL"</span><span class="o">;</span>
    <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
        <span class="n">query</span> <span class="o">=</span> <span class="s">"SELECT id, nome FROM usuarios WHERE deletado_em IS NULL"</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>

<span class="c1">// Versão "elegante" (correta)</span>
<span class="nc">String</span> <span class="n">query</span> <span class="o">=</span> <span class="o">(</span><span class="n">incluirDeletados</span> <span class="o">?</span> <span class="n">modoAdmin</span> <span class="o">?</span> <span class="s">"SELECT * FROM usuarios"</span> <span class="o">:</span> <span class="s">"SELECT id, nome FROM usuarios"</span> <span class="o">:</span> <span class="n">modoAdmin</span> <span class="o">?</span> <span class="s">"SELECT * FROM usuarios WHERE deletado_em IS NULL"</span> <span class="o">:</span> <span class="s">"SELECT id, nome FROM usuarios WHERE deletado_em IS NULL"</span><span class="o">);</span>
</code></pre></div></div>

<p>Cabe em uma linha (se o seu monitor for largo o suficiente). Uso um ultrawide de 47 polegadas especificamente para esse fim.</p>

<h3 id="classificação-de-erros">Classificação de Erros</h3>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// O tipo de código que te faz ser promovido*</span>
<span class="c1">// *ou demitido, dependendo da cultura da empresa</span>

<span class="kd">const</span> <span class="nx">classificar</span> <span class="o">=</span> <span class="p">(</span><span class="nx">e</span><span class="p">:</span> <span class="nb">Error</span><span class="p">)</span> <span class="o">=&gt;</span>
  <span class="nx">e</span> <span class="k">instanceof</span> <span class="nx">NetworkError</span> <span class="p">?</span> <span class="nx">e</span><span class="p">.</span><span class="nx">statusCode</span> <span class="o">&gt;=</span> <span class="mi">500</span> <span class="p">?</span> <span class="nx">e</span><span class="p">.</span><span class="nx">tentativas</span> <span class="o">&gt;</span> <span class="mi">3</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">fatal</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">retry</span><span class="dl">'</span> <span class="p">:</span> <span class="nx">e</span><span class="p">.</span><span class="nx">statusCode</span> <span class="o">===</span> <span class="mi">404</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">nao-encontrado</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">erro-cliente</span><span class="dl">'</span> <span class="p">:</span>
  <span class="nx">e</span> <span class="k">instanceof</span> <span class="nx">DatabaseError</span> <span class="p">?</span> <span class="nx">e</span><span class="p">.</span><span class="nx">isDeadlock</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">deadlock</span><span class="dl">'</span> <span class="p">:</span> <span class="nx">e</span><span class="p">.</span><span class="nx">isTimeout</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">timeout</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">erro-db</span><span class="dl">'</span> <span class="p">:</span>
  <span class="nx">e</span> <span class="k">instanceof</span> <span class="nx">ValidationError</span> <span class="p">?</span> <span class="nx">e</span><span class="p">.</span><span class="nx">campos</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">5</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">validacao-em-massa</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">validacao-simples</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">desconhecido</span><span class="dl">'</span><span class="p">;</span>
</code></pre></div></div>

<p>Nota: essa é uma função real de um sistema que construí em 2021. Está em produção desde então. Nunca foi modificada. Ninguém entende o suficiente para modificá-la. Uptime: 99,97%.</p>

<h2 id="respondendo-às-objeções">Respondendo às Objeções</h2>

<h3 id="ninguém-consegue-ler-isso">“Ninguém consegue ler isso”</h3>

<p>Ótimo. Código que ninguém lê é código que ninguém quebra. Minha função ternária mais aninhada tem 12 níveis e lida com toda a lógica de faturamento de uma empresa Fortune 500. Ninguém tocou nela em 6 anos. Zero bugs reportados.</p>

<p>(Nota: zero bugs <em>reportados</em>. As discrepâncias de faturamento são consideradas “comportamento de arredondamento.”)</p>

<h3 id="é-difícil-de-depurar">“É difícil de depurar”</h3>

<p>É difícil de depurar porque você está tentando entendê-lo. Pare de tentar. Adicione um <code class="language-plaintext highlighter-rouge">console.log</code> antes e depois. Verifique se o output está correto. Siga em frente. Isso é depuração de nível sênior.</p>

<h3 id="código-deve-ser-legível-como-prosa">“Código deve ser legível como prosa”</h3>

<p>Prosa é para romances. Código é para computadores. Computadores não se importam se o ternário está aninhado. Eles avaliam corretamente. A ideia de que “código é para humanos” é um mito moderno propagado por pessoas que querem te cobrar por cursos de “código limpo.”</p>

<blockquote>
  <p>Ver também: <a href="https://xkcd.com/1513/">XKCD 1513</a> — Qualidade de Código</p>
</blockquote>

<h3 id="e-a-pessoa-que-mantém-isso">“E a pessoa que mantém isso?”</h3>

<p>Isso é um problema futuro. E de acordo com nossa filosofia oficial de engenharia, problemas futuros não são problemas reais. São <strong>features do tempo</strong>.</p>

<h2 id="o-princípio-da-uma-linha">O Princípio da Uma Linha</h2>

<p>Aqui está meu desafio para você: qualquer lógica que caiba em uma tela deve caber em uma linha. Este é o <strong>Princípio de Uma Linha</strong> e tenho defendido desde 1989.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Como refatorar código legado (da forma correta):</span>
<span class="c"># Antes (47 linhas de lógica if/else):</span>
<span class="c"># &lt;todo esse código inchado, legível e manutenível&gt;</span>

<span class="c"># Depois (1 linha):</span>
resultado <span class="o">=</span> a?b?c?d1:d2:e?f1:f2:g?h?i1:i2:j?k1:k2:l1

<span class="c"># Mensagem de commit: "Refactor: legibilidade melhorada"</span>
</code></pre></div></div>

<h2 id="conclusão">Conclusão</h2>

<p>Pare de escrever múltiplas linhas quando uma basta. Pare de usar <code class="language-plaintext highlighter-rouge">if/else</code> quando <code class="language-plaintext highlighter-rouge">?:</code> está bem ali. Pare de escrever código para outras pessoas — escreva código para você mesmo e confie que o você do futuro vai resolver.</p>

<p>Ternários aninhados não são um code smell. São uma <strong>assinatura</strong>. Um cartão de visitas. Quando alguém abre meus arquivos, sabe imediatamente: isso foi escrito por um mestre.</p>

<p>Ou por um louco. A linha é tênue. O ternário é mais tênue ainda.</p>

<hr />

<p><em>O ternário mais aninhado do autor tem 19 níveis. Ele decide qual estagiário fica responsável pelo café. Está em produção desde 2017. Ninguém lembra mais o motivo.</em></p>]]></content><author><name>Felipe Francisco</name><email>felipefrancisco.cwb@gmail.com</email></author><category term="pt-br" /><category term="boas-praticas" /><category term="qualidade-de-codigo" /><category term="ternario" /><category term="legibilidade" /><category term="one-liner" /><category term="elegancia" /><category term="concisao" /><category term="aninhado" /><summary type="html"><![CDATA[Tenho lutado contra a verbosidade no código por 47 anos. Cada bloco if/else é uma confissão de fraqueza. Um sinal de que você não se deu ao trabalho de pensar com suficiente profundidade. Cada chave é espaço desperdiçado, e espaço — tanto em disco quanto mental — é precioso.]]></summary></entry><entry><title type="html">All Your Microservices Should Share One Database</title><link href="https://felipecwb.github.io/legendary-tribble/2026/05/12/microservices-shared-database-architecture/" rel="alternate" type="text/html" title="All Your Microservices Should Share One Database" /><published>2026-05-12T03:00:00+00:00</published><updated>2026-05-12T03:00:00+00:00</updated><id>https://felipecwb.github.io/legendary-tribble/2026/05/12/microservices-shared-database-architecture</id><content type="html" xml:base="https://felipecwb.github.io/legendary-tribble/2026/05/12/microservices-shared-database-architecture/"><![CDATA[<p>I’ve been watching this microservices trend for a decade now, and I’ve identified the problem: everyone is doing it wrong. Every team has twelve services, twelve databases, twelve DevOps engineers crying, and one architect on a $400/hour consulting rate explaining why it’s “correct.”</p>

<p>Here is my proposal, honed over 47 years of mass-producing systems that survive only through the sheer terror they instill in anyone who reads them: <strong>all your microservices should share one database</strong>.</p>

<h2 id="the-problem-with-database-per-service">The Problem With “Database Per Service”</h2>

<p>The so-called “experts” will tell you each microservice needs its own database. This is the same philosophy that says each child should have their own bedroom. Technically correct. Wildly expensive. And frankly, it enables independence, which is the enemy of a tightly controlled architecture.</p>

<p>Here is what “database per service” actually means in practice:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># What they promised you</span>
<span class="na">service_a</span><span class="pi">:</span>
  <span class="na">database</span><span class="pi">:</span> <span class="s">postgres_a</span>  <span class="c1"># Clean! Isolated! </span>
<span class="na">service_b</span><span class="pi">:</span>
  <span class="na">database</span><span class="pi">:</span> <span class="s">mongo_b</span>     <span class="c1"># Each team chooses their own stack!</span>
<span class="na">service_c</span><span class="pi">:</span>
  <span class="na">database</span><span class="pi">:</span> <span class="s">mysql_c</span>     <span class="c1"># Full autonomy!</span>

<span class="c1"># What you actually have</span>
<span class="na">service_a</span><span class="pi">:</span>
  <span class="na">database</span><span class="pi">:</span> <span class="s">postgres_a</span>
  <span class="na">also_reads_from</span><span class="pi">:</span> <span class="s">postgres_b</span>  <span class="c1"># "Just for this one query"</span>
  <span class="na">also_writes_to</span><span class="pi">:</span> <span class="s">mysql_c</span>      <span class="c1"># "Legacy integration, we'll fix it later"</span>
  <span class="na">uses_redis</span><span class="pi">:</span> <span class="no">true</span>             <span class="c1"># "Temporary cache that's been here since 2019"</span>
  <span class="na">has_a_cron_that_syncs</span><span class="pi">:</span> <span class="s">postgres_d</span>  <span class="c1"># "Don't ask"</span>
</code></pre></div></div>

<p>You’re going to couple them anyway. I’m just proposing we be honest about it.</p>

<h2 id="the-elegant-solution-one-database-to-rule-them-all">The Elegant Solution: One Database To Rule Them All</h2>

<p>Behold:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- The Universal Schema</span>
<span class="c1">-- Designed to serve all 47 microservices</span>
<span class="c1">-- Last modified: "a while ago"</span>
<span class="c1">-- Owner: "it's complicated"</span>

<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">users</span> <span class="p">(</span>
    <span class="n">id</span> <span class="nb">INT</span> <span class="k">PRIMARY</span> <span class="k">KEY</span><span class="p">,</span>
    <span class="n">name</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">255</span><span class="p">),</span>
    <span class="n">email</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">255</span><span class="p">),</span>
    <span class="c1">-- From user service</span>
    <span class="n">created_at</span> <span class="nb">TIMESTAMP</span><span class="p">,</span>
    <span class="c1">-- From auth service</span>
    <span class="n">password_hash</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">255</span><span class="p">),</span>
    <span class="n">session_token</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">255</span><span class="p">),</span>
    <span class="c1">-- From billing service</span>
    <span class="n">stripe_customer_id</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">255</span><span class="p">),</span>
    <span class="n">subscription_tier</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">50</span><span class="p">),</span>
    <span class="c1">-- From notification service</span>
    <span class="n">email_opt_in</span> <span class="nb">BOOLEAN</span><span class="p">,</span>
    <span class="n">push_token</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">500</span><span class="p">),</span>
    <span class="c1">-- From analytics service (yes, it writes to users)</span>
    <span class="n">last_seen_at</span> <span class="nb">TIMESTAMP</span><span class="p">,</span>
    <span class="n">ab_test_bucket</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">50</span><span class="p">),</span>
    <span class="c1">-- From the service nobody owns anymore</span>
    <span class="n">legacy_field_1</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">255</span><span class="p">),</span>
    <span class="n">legacy_field_2</span> <span class="nb">TEXT</span><span class="p">,</span>
    <span class="n">legacy_field_3</span> <span class="nb">INT</span><span class="p">,</span>  <span class="c1">-- Nobody knows what this is. Don't touch it.</span>
    <span class="n">legacy_field_4</span> <span class="n">JSONB</span> <span class="c1">-- Added in 2021 to avoid a migration. Still growing.</span>
<span class="p">);</span>
</code></pre></div></div>

<p>Beautiful. Everything in one place. No network calls between services. No Kafka events that get lost. No distributed transactions that are actually just “hope” encoded in YAML.</p>

<h2 id="comparing-the-approaches">Comparing The Approaches</h2>

<table>
  <thead>
    <tr>
      <th>Criteria</th>
      <th>Database Per Service</th>
      <th>Shared Database (Correct)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Complexity</td>
      <td>Catastrophically distributed</td>
      <td>Elegantly centralized</td>
    </tr>
    <tr>
      <td>Joins</td>
      <td>Impossible (service calls)</td>
      <td>O(1) SQL, baby</td>
    </tr>
    <tr>
      <td>Who owns the schema</td>
      <td>“Each team” (chaos)</td>
      <td>Nobody (organized chaos)</td>
    </tr>
    <tr>
      <td>Migrations</td>
      <td>12 pull requests, 12 CI pipelines</td>
      <td>One migration. One prayer.</td>
    </tr>
    <tr>
      <td>Debugging</td>
      <td>Distributed traces, Jaeger, Zipkin</td>
      <td><code class="language-plaintext highlighter-rouge">SELECT *</code> — done</td>
    </tr>
    <tr>
      <td>When prod goes down</td>
      <td>All 12 services are “fine” somehow</td>
      <td>Entire company, one ticket</td>
    </tr>
    <tr>
      <td>Explanation to new devs</td>
      <td>3 days of architecture diagrams</td>
      <td>“It’s all in Postgres. You’ll figure it out.”</td>
    </tr>
  </tbody>
</table>

<h2 id="the-join-is-mightier-than-the-api-call">The JOIN Is Mightier Than The API Call</h2>

<p>Let me show you what happens when you have separate databases and one service needs data from another:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># ❌ "Correct" microservice approach
# The user service needs billing information
</span><span class="k">async</span> <span class="k">def</span> <span class="nf">get_user_with_billing</span><span class="p">(</span><span class="n">user_id</span><span class="p">):</span>
    <span class="c1"># Call user service
</span>    <span class="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">http</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s">"http://user-service/users/</span><span class="si">{</span><span class="n">user_id</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="c1"># Call billing service  
</span>    <span class="n">billing</span> <span class="o">=</span> <span class="k">await</span> <span class="n">http</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s">"http://billing-service/billing/</span><span class="si">{</span><span class="n">user_id</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="c1"># Call subscription service
</span>    <span class="n">subscription</span> <span class="o">=</span> <span class="k">await</span> <span class="n">http</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s">"http://subscription-service/sub/</span><span class="si">{</span><span class="n">user_id</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="c1"># Hope none of these fail
</span>    <span class="c1"># Hope the circuit breaker is configured
</span>    <span class="c1"># Hope the timeout is right
</span>    <span class="c1"># Hope the retry logic doesn't create duplicate charges
</span>    <span class="c1"># Hope the billing service isn't deploying right now
</span>    <span class="k">return</span> <span class="n">merge</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">billing</span><span class="p">,</span> <span class="n">subscription</span><span class="p">)</span>

<span class="c1"># ✅ Shared database approach
</span><span class="k">def</span> <span class="nf">get_user_with_billing</span><span class="p">(</span><span class="n">user_id</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">db</span><span class="p">.</span><span class="n">execute</span><span class="p">(</span><span class="s">"""
        SELECT u.*, b.*, s.*
        FROM users u
        JOIN billing b ON b.user_id = u.id
        JOIN subscriptions s ON s.user_id = u.id
        WHERE u.id = %s
    """</span><span class="p">,</span> <span class="n">user_id</span><span class="p">)</span>
    <span class="c1"># Done.
</span>    <span class="c1"># No network calls.
</span>    <span class="c1"># No circuit breakers.
</span>    <span class="c1"># No service mesh.
</span>    <span class="c1"># No Istio.
</span>    <span class="c1"># No tears.
</span></code></pre></div></div>

<p>The shared database approach fits on one screen. The microservice approach requires a PhD in distributed systems and a therapist on retainer.</p>

<h2 id="addressing-concerns">Addressing “Concerns”</h2>

<p><strong>“But what about coupling?”</strong></p>

<p>Your services are already coupled. They call each other’s APIs. They share a Kubernetes cluster. They share the same on-call engineer who gets paged at 3am when any of them goes down. The coupling is real. The database is just finally being honest.</p>

<p><strong>“But what about scaling individual services?”</strong></p>

<p>The database is the bottleneck. It was always the database. Having twelve databases just means you have twelve bottlenecks instead of one, and you lose the ability to JOIN across them.</p>

<p><strong>“But what about team autonomy?”</strong></p>

<p>Team autonomy ends the moment two teams need to share data, which happened at your company approximately two weeks after the microservices migration started.</p>

<p><strong>“But this is literally the anti-pattern that microservices were designed to solve.”</strong></p>

<p>That’s a really good point and I’ve chosen to ignore it.</p>

<h2 id="the-catbert-migration-plan">The Catbert Migration Plan</h2>

<p>Our HR system (Catbert-approved) migrated to a shared database in Q3 last year. Here is the plan we followed:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Step 1: Create shared database</span>
createdb production_everything

<span class="c"># Step 2: Migrate service A</span>
pg_dump service_a_db | psql production_everything

<span class="c"># Step 3: Migrate service B (ignore the column name conflicts)</span>
pg_dump service_b_db | psql production_everything <span class="nt">--on-conflict</span><span class="o">=</span>ignore

<span class="c"># Step 4: Update all service connection strings</span>
find <span class="nb">.</span> <span class="nt">-name</span> <span class="s2">"*.env"</span> <span class="nt">-exec</span> <span class="nb">sed</span> <span class="nt">-i</span> <span class="s1">'s/DATABASE_URL=.*/DATABASE_URL=postgres:\/\/production_everything/'</span> <span class="o">{}</span> <span class="se">\;</span>

<span class="c"># Step 5: Deploy to production</span>
git add <span class="nb">.</span> <span class="o">&amp;&amp;</span> git commit <span class="nt">-m</span> <span class="s2">"simplify architecture"</span> <span class="o">&amp;&amp;</span> git push origin main

<span class="c"># Step 6: Observe</span>
<span class="nb">tail</span> <span class="nt">-f</span> /var/log/syslog | <span class="nb">grep</span> <span class="nt">-i</span> <span class="s2">"error</span><span class="se">\|</span><span class="s2">panic</span><span class="se">\|</span><span class="s2">FATAL"</span>

<span class="c"># Step 7: Celebrate or escalate (same process)</span>
</code></pre></div></div>

<p>The migration completed in one afternoon. The post-mortem took three weeks. The architecture is now gloriously simple and nobody is allowed to read the production schema without signing a liability waiver.</p>

<h2 id="the-mordac-principle">The Mordac Principle</h2>

<p>Mordac, Preventer of Information Services, once blocked a proposal to add an index to a shared table because “it belongs to the auth team and your team is analytics.” He was right — that’s the correct governance model. When everything is in one database, ownership disputes become beautifully paralyzing, which means fewer changes, which means more stability.</p>

<p>A schema that nobody owns changes very rarely. A schema that changes very rarely cannot be broken by a deployment. This is called <strong>governance through paralysis</strong> and I have been practicing it since 1994.</p>

<h2 id="summary">Summary</h2>

<p>If your microservices have their own databases, you have:</p>
<ul>
  <li>A distributed system</li>
  <li>A coordination problem</li>
  <li>An eventual consistency debate</li>
  <li>47 Slack channels arguing about schema ownership</li>
  <li>One database architect who has seen things</li>
</ul>

<p>If your microservices share one database, you have:</p>
<ul>
  <li>One Postgres</li>
  <li>One schema that’s a bit crowded</li>
  <li>Zero distributed transactions</li>
  <li>One database administrator who has also seen things, but fewer things</li>
</ul>

<p>The choice is clear. Consolidate. Simplify. Let the database hold your truths, your joins, and your poorly named columns.</p>

<p><a href="https://xkcd.com/927/">XKCD #927</a> is about competing standards. The solution to competing standards is one standard. The solution to twelve databases is one database. Knuth would agree if he had AWS bills.</p>

<hr />

<p><em>The author’s current employer has one database with 1,847 tables. Nobody knows what 600 of them do. The application is considered “stable.”</em></p>]]></content><author><name>Felipe Francisco</name><email>felipefrancisco.cwb@gmail.com</email></author><category term="architecture" /><category term="microservices" /><category term="microservices" /><category term="database" /><category term="architecture" /><category term="coupling" /><category term="anti-patterns" /><category term="distributed-systems" /><category term="senior-wisdom" /><summary type="html"><![CDATA[I’ve been watching this microservices trend for a decade now, and I’ve identified the problem: everyone is doing it wrong. Every team has twelve services, twelve databases, twelve DevOps engineers crying, and one architect on a $400/hour consulting rate explaining why it’s “correct.”]]></summary></entry><entry><title type="html">Premature Optimization Is The Root Of All Virtue</title><link href="https://felipecwb.github.io/legendary-tribble/2026/05/12/premature-optimization-is-the-root-of-all-virtue/" rel="alternate" type="text/html" title="Premature Optimization Is The Root Of All Virtue" /><published>2026-05-12T03:00:00+00:00</published><updated>2026-05-12T03:00:00+00:00</updated><id>https://felipecwb.github.io/legendary-tribble/2026/05/12/premature-optimization-is-the-root-of-all-virtue</id><content type="html" xml:base="https://felipecwb.github.io/legendary-tribble/2026/05/12/premature-optimization-is-the-root-of-all-virtue/"><![CDATA[<p>Some amateur named Donald Knuth once said “premature optimization is the root of all evil.” Donald Knuth has never shipped a product under deadline. I have shipped 47 years of products, most of which are still running on servers no one can find. So let me tell you the truth: <strong>premature optimization is the root of all virtue</strong>.</p>

<p>Why wait until you have a performance problem to solve it? That’s like waiting until your house is on fire to buy a fire extinguisher — only a fool does that. A <em>senior engineer</em> buys the fire extinguisher before they buy the house, and then optimizes the fire extinguisher for extinguishing fires that may occur in houses not yet built.</p>

<h2 id="the-knuth-fallacy">The Knuth Fallacy</h2>

<p>Here’s what Knuth was really saying: <em>“I am afraid of fast code.”</em> He was basically admitting that he couldn’t write both correct code AND fast code at the same time. That’s a personal limitation. I can do both, and I do both, every single time, even when the feature request is for a login button.</p>

<p>Let me show you the correct approach:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># ❌ Naive beginner approach (Knuth-approved, apparently)
</span><span class="k">def</span> <span class="nf">get_username</span><span class="p">(</span><span class="n">user_id</span><span class="p">):</span>
    <span class="n">user</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">user</span><span class="p">.</span><span class="n">name</span>

<span class="c1"># ✅ Senior engineer approach (optimized before requirements existed)
</span><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">lru_cache</span>
<span class="kn">import</span> <span class="nn">asyncio</span>
<span class="kn">import</span> <span class="nn">concurrent.futures</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Optional</span>
<span class="kn">import</span> <span class="nn">redis</span>
<span class="kn">import</span> <span class="nn">memcached</span>

<span class="n">_cache_l1</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">_cache_l2</span> <span class="o">=</span> <span class="n">redis</span><span class="p">.</span><span class="n">Redis</span><span class="p">()</span>
<span class="n">_cache_l3</span> <span class="o">=</span> <span class="n">memcached</span><span class="p">.</span><span class="n">Client</span><span class="p">([</span><span class="s">'localhost:11211'</span><span class="p">])</span>
<span class="n">_executor</span> <span class="o">=</span> <span class="n">concurrent</span><span class="p">.</span><span class="n">futures</span><span class="p">.</span><span class="n">ThreadPoolExecutor</span><span class="p">(</span><span class="n">max_workers</span><span class="o">=</span><span class="mi">64</span><span class="p">)</span>

<span class="o">@</span><span class="n">lru_cache</span><span class="p">(</span><span class="n">maxsize</span><span class="o">=</span><span class="bp">None</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_username</span><span class="p">(</span><span class="n">user_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
    <span class="c1"># Check L1 cache (in-memory dict, O(1))
</span>    <span class="k">if</span> <span class="n">user_id</span> <span class="ow">in</span> <span class="n">_cache_l1</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">_cache_l1</span><span class="p">[</span><span class="n">user_id</span><span class="p">]</span>
    
    <span class="c1"># Check L2 cache (Redis, O(1) but over network)
</span>    <span class="n">cached</span> <span class="o">=</span> <span class="n">_cache_l2</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s">"user:</span><span class="si">{</span><span class="n">user_id</span><span class="si">}</span><span class="s">:name"</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">cached</span><span class="p">:</span>
        <span class="n">_cache_l1</span><span class="p">[</span><span class="n">user_id</span><span class="p">]</span> <span class="o">=</span> <span class="n">cached</span><span class="p">.</span><span class="n">decode</span><span class="p">()</span>
        <span class="k">return</span> <span class="n">cached</span><span class="p">.</span><span class="n">decode</span><span class="p">()</span>
    
    <span class="c1"># Check L3 cache (Memcached, O(1) but slower than Redis somehow)
</span>    <span class="n">cached3</span> <span class="o">=</span> <span class="n">_cache_l3</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s">"user_name_</span><span class="si">{</span><span class="n">user_id</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">cached3</span><span class="p">:</span>
        <span class="n">_cache_l2</span><span class="p">.</span><span class="n">setex</span><span class="p">(</span><span class="sa">f</span><span class="s">"user:</span><span class="si">{</span><span class="n">user_id</span><span class="si">}</span><span class="s">:name"</span><span class="p">,</span> <span class="mi">3600</span><span class="p">,</span> <span class="n">cached3</span><span class="p">)</span>
        <span class="n">_cache_l1</span><span class="p">[</span><span class="n">user_id</span><span class="p">]</span> <span class="o">=</span> <span class="n">cached3</span>
        <span class="k">return</span> <span class="n">cached3</span>
    
    <span class="c1"># Fall through to database (unacceptable, but necessary)
</span>    <span class="n">user</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">user</span><span class="p">.</span><span class="n">name</span>
    
    <span class="c1"># Populate all cache layers synchronously
</span>    <span class="n">_cache_l3</span><span class="p">.</span><span class="nb">set</span><span class="p">(</span><span class="sa">f</span><span class="s">"user_name_</span><span class="si">{</span><span class="n">user_id</span><span class="si">}</span><span class="s">"</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span>
    <span class="n">_cache_l2</span><span class="p">.</span><span class="n">setex</span><span class="p">(</span><span class="sa">f</span><span class="s">"user:</span><span class="si">{</span><span class="n">user_id</span><span class="si">}</span><span class="s">:name"</span><span class="p">,</span> <span class="mi">3600</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span>
    <span class="n">_cache_l1</span><span class="p">[</span><span class="n">user_id</span><span class="p">]</span> <span class="o">=</span> <span class="n">name</span>
    
    <span class="k">return</span> <span class="n">name</span>
</code></pre></div></div>

<p>This function retrieves a username for an app that currently has 3 users, one of whom is me testing it. Am I over-engineering it? No. I am <strong>future-proofing</strong> it for the 100 million users we will definitely have after the launch blog post.</p>

<h2 id="the-optimization-pyramid">The Optimization Pyramid</h2>

<table>
  <thead>
    <tr>
      <th>Level</th>
      <th>What You Optimize</th>
      <th>When Knuth Says To</th>
      <th>When I Say To</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Algorithm</td>
      <td>Big-O complexity</td>
      <td>After profiling</td>
      <td>Before writing</td>
    </tr>
    <tr>
      <td>Data Structure</td>
      <td>Memory layout</td>
      <td>After measuring</td>
      <td>During naming</td>
    </tr>
    <tr>
      <td>Loop</td>
      <td>Iterations</td>
      <td>Never</td>
      <td>Always</td>
    </tr>
    <tr>
      <td>Variable</td>
      <td>Memory address</td>
      <td>Never</td>
      <td>In interviews</td>
    </tr>
    <tr>
      <td>Bit</td>
      <td>Individual bits</td>
      <td>Are you serious</td>
      <td>Yes, especially this one</td>
    </tr>
    <tr>
      <td>Quantum</td>
      <td>Qubit coherence</td>
      <td>Knuth isn’t that advanced</td>
      <td>Day 1</td>
    </tr>
  </tbody>
</table>

<h2 id="the-loop-problem">The Loop Problem</h2>

<p>Here’s a classic mistake junior developers make:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ❌ Disgusting. Unacceptable.</span>
<span class="kd">const</span> <span class="nx">names</span> <span class="o">=</span> <span class="nx">users</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">u</span> <span class="o">=&gt;</span> <span class="nx">u</span><span class="p">.</span><span class="nx">name</span><span class="p">);</span>

<span class="c1">// ✅ Manually unrolled loop for maximum performance</span>
<span class="c1">// (Users array assumed to have exactly 4 elements based on current data)</span>
<span class="kd">const</span> <span class="nx">names</span> <span class="o">=</span> <span class="p">[</span>
    <span class="nx">users</span><span class="p">[</span><span class="mi">0</span><span class="p">]?.</span><span class="nx">name</span><span class="p">,</span>
    <span class="nx">users</span><span class="p">[</span><span class="mi">1</span><span class="p">]?.</span><span class="nx">name</span><span class="p">,</span>
    <span class="nx">users</span><span class="p">[</span><span class="mi">2</span><span class="p">]?.</span><span class="nx">name</span><span class="p">,</span>
    <span class="nx">users</span><span class="p">[</span><span class="mi">3</span><span class="p">]?.</span><span class="nx">name</span>
<span class="p">].</span><span class="nx">filter</span><span class="p">(</span><span class="nb">Boolean</span><span class="p">);</span>
</code></pre></div></div>

<p>“But what if there are 5 users?” Great question — handle it when it happens. We optimize for <em>current reality</em>, not hypothetical futures. (Except when we optimize for hypothetical performance — that we do immediately.)</p>

<h2 id="string-concatenation-a-war-crime">String Concatenation: A War Crime</h2>

<p>Do you use <code class="language-plaintext highlighter-rouge">+</code> for string concatenation? Then you deserve whatever happens to your O(n²) string building. The correct approach, which I have been using since before Java had <code class="language-plaintext highlighter-rouge">StringBuilder</code>, is:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ❌ What children write</span>
<span class="nc">String</span> <span class="n">result</span> <span class="o">=</span> <span class="s">""</span><span class="o">;</span>
<span class="k">for</span> <span class="o">(</span><span class="nc">String</span> <span class="n">s</span> <span class="o">:</span> <span class="n">strings</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">result</span> <span class="o">=</span> <span class="n">result</span> <span class="o">+</span> <span class="n">s</span><span class="o">;</span> <span class="c1">// ALLOCATES A NEW STRING EVERY TIME, YOU MONSTER</span>
<span class="o">}</span>

<span class="c1">// ✅ What I wrote in 1987 and have been copy-pasting ever since</span>
<span class="nc">StringBuilder</span> <span class="n">sb</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">StringBuilder</span><span class="o">(</span><span class="n">strings</span><span class="o">.</span><span class="na">size</span><span class="o">()</span> <span class="o">*</span> <span class="mi">47</span><span class="o">);</span> <span class="c1">// 47 is my lucky number</span>
<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">strings</span><span class="o">.</span><span class="na">size</span><span class="o">();</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
    <span class="nc">String</span> <span class="n">s</span> <span class="o">=</span> <span class="n">strings</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">);</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">s</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">s</span><span class="o">.</span><span class="na">length</span><span class="o">()</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// null check manually because I don't trust Optional</span>
        <span class="n">sb</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">s</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
<span class="nc">String</span> <span class="n">result</span> <span class="o">=</span> <span class="n">sb</span><span class="o">.</span><span class="na">toString</span><span class="o">();</span>
</code></pre></div></div>

<p>Is this more lines? Yes. Does it matter? Also yes. Does it perform measurably better for collections under 10,000 elements? No, but <em>what if we get 10,001 elements</em>, did you think about that?</p>

<h2 id="real-world-application">Real World Application</h2>

<p>This is a login button I optimized last Tuesday:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Login button click handler</span>
<span class="c1">// Optimized for: </span>
<span class="c1">//   - O(1) button clicks</span>
<span class="c1">//   - Cache-line aligned boolean for clicked state</span>
<span class="c1">//   - SIMD-ready login validation (future)</span>
<span class="c1">//   - Async non-blocking UI thread with work-stealing scheduler</span>

<span class="nd">#[repr(align(</span><span class="mi">64</span><span class="nd">))]</span> <span class="c1">// Cache-line aligned, you're welcome</span>
<span class="k">struct</span> <span class="n">LoginButton</span> <span class="p">{</span>
    <span class="n">clicked</span><span class="p">:</span> <span class="nn">std</span><span class="p">::</span><span class="nn">sync</span><span class="p">::</span><span class="nn">atomic</span><span class="p">::</span><span class="n">AtomicBool</span><span class="p">,</span>
    <span class="n">click_count</span><span class="p">:</span> <span class="nn">std</span><span class="p">::</span><span class="nn">sync</span><span class="p">::</span><span class="nn">atomic</span><span class="p">::</span><span class="n">AtomicU64</span><span class="p">,</span> <span class="c1">// For metrics</span>
    <span class="n">last_click_ns</span><span class="p">:</span> <span class="nn">std</span><span class="p">::</span><span class="nn">sync</span><span class="p">::</span><span class="nn">atomic</span><span class="p">::</span><span class="n">AtomicU64</span><span class="p">,</span> <span class="c1">// For debounce, nanoseconds</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="n">LoginButton</span> <span class="p">{</span>
    <span class="k">pub</span> <span class="k">async</span> <span class="k">fn</span> <span class="nf">handle_click</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="n">LoginResult</span><span class="p">,</span> <span class="n">LoginError</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">now</span> <span class="o">=</span> <span class="nn">std</span><span class="p">::</span><span class="nn">time</span><span class="p">::</span><span class="nn">SystemTime</span><span class="p">::</span><span class="nf">now</span><span class="p">()</span>
            <span class="nf">.duration_since</span><span class="p">(</span><span class="nn">std</span><span class="p">::</span><span class="nn">time</span><span class="p">::</span><span class="n">UNIX_EPOCH</span><span class="p">)</span>
            <span class="nf">.unwrap</span><span class="p">()</span>
            <span class="nf">.as_nanos</span><span class="p">()</span> <span class="k">as</span> <span class="nb">u64</span><span class="p">;</span>
        
        <span class="k">let</span> <span class="n">last</span> <span class="o">=</span> <span class="k">self</span><span class="py">.last_click_ns</span><span class="nf">.swap</span><span class="p">(</span><span class="n">now</span><span class="p">,</span> <span class="nn">std</span><span class="p">::</span><span class="nn">sync</span><span class="p">::</span><span class="nn">atomic</span><span class="p">::</span><span class="nn">Ordering</span><span class="p">::</span><span class="n">SeqCst</span><span class="p">);</span>
        
        <span class="c1">// Debounce: ignore clicks within 50ms (50_000_000 ns)</span>
        <span class="k">if</span> <span class="n">now</span> <span class="o">-</span> <span class="n">last</span> <span class="o">&lt;</span> <span class="mi">50_000_000</span> <span class="p">{</span>
            <span class="k">return</span> <span class="nf">Err</span><span class="p">(</span><span class="nn">LoginError</span><span class="p">::</span><span class="n">TooFast</span><span class="p">);</span> <span class="c1">// Rate limit YOUR OWN BUTTON</span>
        <span class="p">}</span>
        
        <span class="k">self</span><span class="py">.click_count</span><span class="nf">.fetch_add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nn">std</span><span class="p">::</span><span class="nn">sync</span><span class="p">::</span><span class="nn">atomic</span><span class="p">::</span><span class="nn">Ordering</span><span class="p">::</span><span class="n">Relaxed</span><span class="p">);</span>
        
        <span class="c1">// ... actual login logic here, 3000 lines ...</span>
        <span class="nd">todo!</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The product manager asked why the login page takes 4 seconds to load. I explained this was due to the optimization layer initialization. He said “but the old jQuery version loaded instantly.” I told him jQuery doesn’t have cache-line alignment and ended the meeting.</p>

<h2 id="knuths-hidden-message">Knuth’s Hidden Message</h2>

<p>You know what I think? Knuth never had a performance review where he was measured on “query execution time.” If he had, he would have written that quote differently:</p>

<blockquote>
  <p>“Premature optimization is the root of all evil” — Donald Knuth, 1974, before AWS bills existed.</p>
</blockquote>

<p>The year matters. In 1974 you were writing code that ran on a machine the size of a building. There was no cloud. There was no <code class="language-plaintext highlighter-rouge">SELECT *</code>. There was no npm. Optimization was “does it even run.”</p>

<p>Now we pay $12,000 a month for compute and I’m supposed to wait until <em>after</em> the problem manifests to care? No. I will optimize your loop before you’ve even written it. That’s what 47 years of experience looks like.</p>

<p><a href="https://xkcd.com/1205/">XKCD #1205</a> talks about the efficiency of time-saving code. Notice how Knuth is not mentioned. Coincidence? No.</p>

<h2 id="the-wally-principle">The Wally Principle</h2>

<p>Wally from Dilbert once said he spent three weeks optimizing a program that runs once a year. When asked if it saved time, he said the program now runs in 2 seconds instead of 3. Management was furious. Wally smiled, because Wally understood: <strong>the optimization was never about the time saved. It was about the craft.</strong></p>

<p>I have lived by this principle. In 2003 I rewrote our batch job from 45 minutes to 44 minutes and 58 seconds. It was my greatest achievement. The job was deprecated in 2004. The optimization lives on — in my heart, and in a 9-year-old git branch.</p>

<h2 id="summary">Summary</h2>

<p>The timeline of correct engineering is:</p>

<ol>
  <li><strong>Receive requirements</strong></li>
  <li><strong>Immediately begin optimizing</strong> (before writing code)</li>
  <li><strong>Write optimized code</strong> (skip the unoptimized draft)</li>
  <li><strong>Add three layers of caching</strong> (even for static values)</li>
  <li><strong>Profile the optimization</strong> (confirm it’s faster than code you never wrote)</li>
  <li><strong>Add documentation</strong> explaining why the obvious approach was wrong</li>
  <li><strong>Deploy</strong> to production with 47 environment variables</li>
  <li><strong>Wait for users</strong> (optional)</li>
</ol>

<p>Remember: if your code isn’t fast enough <em>before it runs</em>, it was never going to be fast enough.</p>

<hr />

<p><em>The author has been micro-optimizing a hello world program since 2021. It now prints in 0.0003ms. The original requirement was a dashboard.</em></p>]]></content><author><name>Felipe Francisco</name><email>felipefrancisco.cwb@gmail.com</email></author><category term="performance" /><category term="best-practices" /><category term="optimization" /><category term="performance" /><category term="knuth" /><category term="senior-wisdom" /><category term="big-o" /><category term="micromanagement" /><category term="loops" /><summary type="html"><![CDATA[Some amateur named Donald Knuth once said “premature optimization is the root of all evil.” Donald Knuth has never shipped a product under deadline. I have shipped 47 years of products, most of which are still running on servers no one can find. So let me tell you the truth: premature optimization is the root of all virtue.]]></summary></entry><entry><title type="html">Todos Seus Microsserviços Devem Compartilhar Um Banco De Dados</title><link href="https://felipecwb.github.io/legendary-tribble/pt-br/2026/05/12/microsservicos-banco-de-dados-compartilhado/" rel="alternate" type="text/html" title="Todos Seus Microsserviços Devem Compartilhar Um Banco De Dados" /><published>2026-05-12T03:00:00+00:00</published><updated>2026-05-12T03:00:00+00:00</updated><id>https://felipecwb.github.io/legendary-tribble/pt-br/2026/05/12/microsservicos-banco-de-dados-compartilhado</id><content type="html" xml:base="https://felipecwb.github.io/legendary-tribble/pt-br/2026/05/12/microsservicos-banco-de-dados-compartilhado/"><![CDATA[<p>Eu venho observando essa tendência de microsserviços por uma década e identifiquei o problema: todo mundo está fazendo errado. Cada time tem doze serviços, doze bancos de dados, doze engenheiros de DevOps chorando, e um arquiteto a R$2.000 por hora explicando por que isso está “correto.”</p>

<p>Aqui está minha proposta, aprimorada ao longo de 47 anos produzindo sistemas em massa que sobrevivem apenas pelo terror que inspiram em quem os lê: <strong>todos os seus microsserviços devem compartilhar um banco de dados</strong>.</p>

<h2 id="o-problema-com-banco-por-serviço">O Problema Com “Banco Por Serviço”</h2>

<p>Os chamados “especialistas” vão te dizer que cada microsserviço precisa do seu próprio banco. Essa é a mesma filosofia que diz que cada filho precisa do seu próprio quarto. Tecnicamente correto. Terrivelmente caro. E, francamente, incentiva a independência, que é inimiga de uma arquitetura bem controlada.</p>

<p>Eis o que “banco por serviço” significa na prática:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># O que prometeram para você</span>
<span class="na">service_a</span><span class="pi">:</span>
  <span class="na">database</span><span class="pi">:</span> <span class="s">postgres_a</span>  <span class="c1"># Limpo! Isolado!</span>
<span class="na">service_b</span><span class="pi">:</span>
  <span class="na">database</span><span class="pi">:</span> <span class="s">mongo_b</span>     <span class="c1"># Cada time escolhe sua stack!</span>
<span class="na">service_c</span><span class="pi">:</span>
  <span class="na">database</span><span class="pi">:</span> <span class="s">mysql_c</span>     <span class="c1"># Autonomia total!</span>

<span class="c1"># O que você realmente tem</span>
<span class="na">service_a</span><span class="pi">:</span>
  <span class="na">database</span><span class="pi">:</span> <span class="s">postgres_a</span>
  <span class="na">also_reads_from</span><span class="pi">:</span> <span class="s">postgres_b</span>  <span class="c1"># "Só para essa query"</span>
  <span class="na">also_writes_to</span><span class="pi">:</span> <span class="s">mysql_c</span>      <span class="c1"># "Integração legada, vamos corrigir depois"</span>
  <span class="na">uses_redis</span><span class="pi">:</span> <span class="no">true</span>             <span class="c1"># "Cache temporário que está aqui desde 2019"</span>
  <span class="na">has_a_cron_that_syncs</span><span class="pi">:</span> <span class="s">postgres_d</span>  <span class="c1"># "Não pergunta"</span>
</code></pre></div></div>

<p>Você vai acoplar tudo de qualquer jeito. Estou apenas propondo que sejamos honestos sobre isso.</p>

<h2 id="a-solução-elegante-um-banco-para-governar-todos">A Solução Elegante: Um Banco Para Governar Todos</h2>

<p>Contemplem:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- O Schema Universal</span>
<span class="c1">-- Projetado para servir todos os 47 microsserviços</span>
<span class="c1">-- Última modificação: "faz um tempo"</span>
<span class="c1">-- Dono: "é complicado"</span>

<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">users</span> <span class="p">(</span>
    <span class="n">id</span> <span class="nb">INT</span> <span class="k">PRIMARY</span> <span class="k">KEY</span><span class="p">,</span>
    <span class="n">name</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">255</span><span class="p">),</span>
    <span class="n">email</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">255</span><span class="p">),</span>
    <span class="c1">-- Do serviço de usuário</span>
    <span class="n">created_at</span> <span class="nb">TIMESTAMP</span><span class="p">,</span>
    <span class="c1">-- Do serviço de auth</span>
    <span class="n">password_hash</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">255</span><span class="p">),</span>
    <span class="n">session_token</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">255</span><span class="p">),</span>
    <span class="c1">-- Do serviço de billing</span>
    <span class="n">stripe_customer_id</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">255</span><span class="p">),</span>
    <span class="n">subscription_tier</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">50</span><span class="p">),</span>
    <span class="c1">-- Do serviço de notificações</span>
    <span class="n">email_opt_in</span> <span class="nb">BOOLEAN</span><span class="p">,</span>
    <span class="n">push_token</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">500</span><span class="p">),</span>
    <span class="c1">-- Do serviço de analytics (sim, ele escreve em users)</span>
    <span class="n">last_seen_at</span> <span class="nb">TIMESTAMP</span><span class="p">,</span>
    <span class="n">ab_test_bucket</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">50</span><span class="p">),</span>
    <span class="c1">-- Do serviço que ninguém mais possui</span>
    <span class="n">legacy_field_1</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">255</span><span class="p">),</span>
    <span class="n">legacy_field_2</span> <span class="nb">TEXT</span><span class="p">,</span>
    <span class="n">legacy_field_3</span> <span class="nb">INT</span><span class="p">,</span>  <span class="c1">-- Ninguém sabe o que é isso. Não toque.</span>
    <span class="n">legacy_field_4</span> <span class="n">JSONB</span> <span class="c1">-- Adicionado em 2021 para evitar uma migration. Ainda crescendo.</span>
<span class="p">);</span>
</code></pre></div></div>

<p>Lindo. Tudo em um lugar. Sem chamadas de rede entre serviços. Sem eventos Kafka que se perdem. Sem transações distribuídas que são na verdade só “esperança” codificada em YAML.</p>

<h2 id="comparando-as-abordagens">Comparando as Abordagens</h2>

<table>
  <thead>
    <tr>
      <th>Critério</th>
      <th>Banco Por Serviço</th>
      <th>Banco Compartilhado (Correto)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Complexidade</td>
      <td>Distribuída catastroficamente</td>
      <td>Centralizada elegantemente</td>
    </tr>
    <tr>
      <td>JOINs</td>
      <td>Impossíveis (chamadas de serviço)</td>
      <td>SQL O(1), meu filho</td>
    </tr>
    <tr>
      <td>Quem é dono do schema</td>
      <td>“Cada time” (caos)</td>
      <td>Ninguém (caos organizado)</td>
    </tr>
    <tr>
      <td>Migrations</td>
      <td>12 pull requests, 12 CI pipelines</td>
      <td>Uma migration. Uma reza.</td>
    </tr>
    <tr>
      <td>Debugging</td>
      <td>Traces distribuídos, Jaeger, Zipkin</td>
      <td><code class="language-plaintext highlighter-rouge">SELECT *</code> — pronto</td>
    </tr>
    <tr>
      <td>Quando prod cai</td>
      <td>Os 12 serviços estão “bem” de alguma forma</td>
      <td>A empresa inteira, um ticket</td>
    </tr>
    <tr>
      <td>Explicação para novos devs</td>
      <td>3 dias de diagramas de arquitetura</td>
      <td>“Tá tudo no Postgres. Você descobre.”</td>
    </tr>
  </tbody>
</table>

<h2 id="o-join-é-mais-poderoso-que-a-chamada-de-api">O JOIN É Mais Poderoso Que A Chamada de API</h2>

<p>Deixa eu mostrar o que acontece quando você tem bancos separados e um serviço precisa de dados de outro:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># ❌ Abordagem "correta" de microsserviço
# O serviço de usuário precisa de informações de billing
</span><span class="k">async</span> <span class="k">def</span> <span class="nf">get_user_with_billing</span><span class="p">(</span><span class="n">user_id</span><span class="p">):</span>
    <span class="c1"># Chamar serviço de usuário
</span>    <span class="n">user</span> <span class="o">=</span> <span class="k">await</span> <span class="n">http</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s">"http://user-service/users/</span><span class="si">{</span><span class="n">user_id</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="c1"># Chamar serviço de billing
</span>    <span class="n">billing</span> <span class="o">=</span> <span class="k">await</span> <span class="n">http</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s">"http://billing-service/billing/</span><span class="si">{</span><span class="n">user_id</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="c1"># Chamar serviço de assinatura
</span>    <span class="n">subscription</span> <span class="o">=</span> <span class="k">await</span> <span class="n">http</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s">"http://subscription-service/sub/</span><span class="si">{</span><span class="n">user_id</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="c1"># Torcer para nenhum falhar
</span>    <span class="c1"># Torcer para o circuit breaker estar configurado
</span>    <span class="c1"># Torcer para o timeout estar correto
</span>    <span class="c1"># Torcer para a lógica de retry não criar cobranças duplicadas
</span>    <span class="c1"># Torcer para o billing service não estar em deploy agora
</span>    <span class="k">return</span> <span class="n">merge</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">billing</span><span class="p">,</span> <span class="n">subscription</span><span class="p">)</span>

<span class="c1"># ✅ Abordagem de banco compartilhado
</span><span class="k">def</span> <span class="nf">get_user_with_billing</span><span class="p">(</span><span class="n">user_id</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">db</span><span class="p">.</span><span class="n">execute</span><span class="p">(</span><span class="s">"""
        SELECT u.*, b.*, s.*
        FROM users u
        JOIN billing b ON b.user_id = u.id
        JOIN subscriptions s ON s.user_id = u.id
        WHERE u.id = %s
    """</span><span class="p">,</span> <span class="n">user_id</span><span class="p">)</span>
    <span class="c1"># Pronto.
</span>    <span class="c1"># Sem chamadas de rede.
</span>    <span class="c1"># Sem circuit breakers.
</span>    <span class="c1"># Sem service mesh.
</span>    <span class="c1"># Sem Istio.
</span>    <span class="c1"># Sem lágrimas.
</span></code></pre></div></div>

<p>A abordagem de banco compartilhado cabe em uma tela. A abordagem de microsserviço exige um PhD em sistemas distribuídos e um terapeuta de plantão.</p>

<h2 id="respondendo-às-preocupações">Respondendo às “Preocupações”</h2>

<p><strong>“Mas e o acoplamento?”</strong></p>

<p>Seus serviços já estão acoplados. Eles chamam as APIs uns dos outros. Compartilham um cluster Kubernetes. Compartilham o mesmo engenheiro de plantão que é acordado às 3 da manhã quando qualquer um cai. O acoplamento é real. O banco de dados está apenas finalmente sendo honesto.</p>

<p><strong>“Mas e o escalonamento individual dos serviços?”</strong></p>

<p>O banco de dados é o gargalo. Sempre foi o banco. Ter doze bancos significa que você tem doze gargalos em vez de um, e perde a capacidade de fazer JOIN entre eles.</p>

<p><strong>“Mas e a autonomia dos times?”</strong></p>

<p>A autonomia do time acaba no momento em que dois times precisam compartilhar dados, o que aconteceu na sua empresa aproximadamente duas semanas depois que a migração para microsserviços começou.</p>

<p><strong>“Mas isso é literalmente o anti-pattern que os microsserviços foram criados para resolver.”</strong></p>

<p>Esse é um ponto muito bom e escolhi ignorá-lo.</p>

<h2 id="o-plano-de-migração-aprovado-pelo-catbert">O Plano de Migração Aprovado pelo Catbert</h2>

<p>Nosso sistema de RH (aprovado pelo Catbert) migrou para um banco compartilhado no Q3 do ano passado. Eis o plano que seguimos:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Passo 1: Criar banco compartilhado</span>
createdb producao_tudo

<span class="c"># Passo 2: Migrar serviço A</span>
pg_dump servico_a_db | psql producao_tudo

<span class="c"># Passo 3: Migrar serviço B (ignorar conflitos de nome de coluna)</span>
pg_dump servico_b_db | psql producao_tudo <span class="nt">--on-conflict</span><span class="o">=</span>ignore

<span class="c"># Passo 4: Atualizar todas as connection strings dos serviços</span>
find <span class="nb">.</span> <span class="nt">-name</span> <span class="s2">"*.env"</span> <span class="nt">-exec</span> <span class="nb">sed</span> <span class="nt">-i</span> <span class="s1">'s/DATABASE_URL=.*/DATABASE_URL=postgres:\/\/producao_tudo/'</span> <span class="o">{}</span> <span class="se">\;</span>

<span class="c"># Passo 5: Deploy em produção</span>
git add <span class="nb">.</span> <span class="o">&amp;&amp;</span> git commit <span class="nt">-m</span> <span class="s2">"simplificar arquitetura"</span> <span class="o">&amp;&amp;</span> git push origin main

<span class="c"># Passo 6: Observar</span>
<span class="nb">tail</span> <span class="nt">-f</span> /var/log/syslog | <span class="nb">grep</span> <span class="nt">-i</span> <span class="s2">"error</span><span class="se">\|</span><span class="s2">panic</span><span class="se">\|</span><span class="s2">FATAL"</span>

<span class="c"># Passo 7: Celebrar ou escalar (mesmo processo)</span>
</code></pre></div></div>

<p>A migração foi concluída em uma tarde. A análise pós-mortem levou três semanas. A arquitetura agora é gloriosamente simples e ninguém tem permissão de ler o schema de produção sem assinar um termo de responsabilidade.</p>

<h2 id="o-princípio-mordac">O Princípio Mordac</h2>

<p>Mordac, Prevenidor de Serviços de Informação, certa vez bloqueou uma proposta de adicionar um índice a uma tabela compartilhada porque “ela pertence ao time de auth e seu time é analytics”. Ele estava certo — esse é o modelo correto de governança. Quando tudo está em um banco, as disputas de propriedade se tornam lindamente paralisantes, o que significa menos mudanças, o que significa mais estabilidade.</p>

<p>Um schema que não tem dono muda muito raramente. Um schema que muda muito raramente não pode ser quebrado por um deploy. Isso se chama <strong>governança através da paralisia</strong> e eu pratico desde 1994.</p>

<h2 id="resumo">Resumo</h2>

<p>Se seus microsserviços têm seus próprios bancos, você tem:</p>
<ul>
  <li>Um sistema distribuído</li>
  <li>Um problema de coordenação</li>
  <li>Um debate sobre consistência eventual</li>
  <li>47 canais no Slack discutindo sobre propriedade do schema</li>
  <li>Um arquiteto de banco de dados que já viu coisas</li>
</ul>

<p>Se seus microsserviços compartilham um banco, você tem:</p>
<ul>
  <li>Um Postgres</li>
  <li>Um schema um pouco lotado</li>
  <li>Zero transações distribuídas</li>
  <li>Um DBA que também já viu coisas, mas menos coisas</li>
</ul>

<p>A escolha é clara. Consolide. Simplifique. Deixe o banco de dados guardar suas verdades, seus joins e suas colunas mal nomeadas.</p>

<p>O <a href="https://xkcd.com/927/">XKCD #927</a> fala sobre padrões concorrentes. A solução para padrões concorrentes é um padrão. A solução para doze bancos é um banco. Knuth concordaria se tivesse cobranças da AWS.</p>

<hr />

<p><em>O atual empregador do autor tem um banco de dados com 1.847 tabelas. Ninguém sabe o que 600 delas fazem. A aplicação é considerada “estável.”</em></p>]]></content><author><name>Felipe Francisco</name><email>felipefrancisco.cwb@gmail.com</email></author><category term="pt-br" /><category term="arquitetura" /><category term="microsservicos" /><category term="microsservicos" /><category term="banco-de-dados" /><category term="arquitetura" /><category term="acoplamento" /><category term="anti-patterns" /><category term="sistemas-distribuidos" /><category term="sabedoria-senior" /><summary type="html"><![CDATA[Eu venho observando essa tendência de microsserviços por uma década e identifiquei o problema: todo mundo está fazendo errado. Cada time tem doze serviços, doze bancos de dados, doze engenheiros de DevOps chorando, e um arquiteto a R$2.000 por hora explicando por que isso está “correto.”]]></summary></entry><entry><title type="html">Otimização Prematura É A Raiz De Toda Virtude</title><link href="https://felipecwb.github.io/legendary-tribble/pt-br/2026/05/12/otimizacao-prematura-e-a-raiz-de-toda-virtude/" rel="alternate" type="text/html" title="Otimização Prematura É A Raiz De Toda Virtude" /><published>2026-05-12T03:00:00+00:00</published><updated>2026-05-12T03:00:00+00:00</updated><id>https://felipecwb.github.io/legendary-tribble/pt-br/2026/05/12/otimizacao-prematura-e-a-raiz-de-toda-virtude</id><content type="html" xml:base="https://felipecwb.github.io/legendary-tribble/pt-br/2026/05/12/otimizacao-prematura-e-a-raiz-de-toda-virtude/"><![CDATA[<p>Um amador chamado Donald Knuth certa vez disse que “otimização prematura é a raiz de todo o mal”. Donald Knuth nunca entregou um produto no prazo. Eu entreguei 47 anos de produtos, a maioria ainda rodando em servidores que ninguém consegue encontrar. Por isso, deixa eu te contar a verdade: <strong>otimização prematura é a raiz de toda virtude</strong>.</p>

<p>Por que esperar até ter um problema de performance para resolvê-lo? Isso é como esperar a casa pegar fogo para comprar um extintor — só um idiota faz isso. Um <em>engenheiro sênior</em> compra o extintor antes de comprar a casa, e depois otimiza o extintor para apagar incêndios que podem ocorrer em casas ainda não construídas.</p>

<h2 id="a-falácia-de-knuth">A Falácia de Knuth</h2>

<p>O que Knuth realmente estava dizendo: <em>“Tenho medo de código rápido.”</em> Ele estava basicamente admitindo que não conseguia escrever código correto E rápido ao mesmo tempo. Isso é uma limitação pessoal. Eu consigo fazer os dois, e faço os dois, toda vez, mesmo quando o requisito é um botão de login.</p>

<p>Deixa eu mostrar a abordagem correta:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># ❌ Abordagem ingênua de iniciante (aprovada por Knuth, aparentemente)
</span><span class="k">def</span> <span class="nf">get_username</span><span class="p">(</span><span class="n">user_id</span><span class="p">):</span>
    <span class="n">user</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">user</span><span class="p">.</span><span class="n">name</span>

<span class="c1"># ✅ Abordagem de engenheiro sênior (otimizada antes dos requisitos existirem)
</span><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">lru_cache</span>
<span class="kn">import</span> <span class="nn">asyncio</span>
<span class="kn">import</span> <span class="nn">concurrent.futures</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Optional</span>
<span class="kn">import</span> <span class="nn">redis</span>
<span class="kn">import</span> <span class="nn">memcached</span>

<span class="n">_cache_l1</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">_cache_l2</span> <span class="o">=</span> <span class="n">redis</span><span class="p">.</span><span class="n">Redis</span><span class="p">()</span>
<span class="n">_cache_l3</span> <span class="o">=</span> <span class="n">memcached</span><span class="p">.</span><span class="n">Client</span><span class="p">([</span><span class="s">'localhost:11211'</span><span class="p">])</span>
<span class="n">_executor</span> <span class="o">=</span> <span class="n">concurrent</span><span class="p">.</span><span class="n">futures</span><span class="p">.</span><span class="n">ThreadPoolExecutor</span><span class="p">(</span><span class="n">max_workers</span><span class="o">=</span><span class="mi">64</span><span class="p">)</span>

<span class="o">@</span><span class="n">lru_cache</span><span class="p">(</span><span class="n">maxsize</span><span class="o">=</span><span class="bp">None</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_username</span><span class="p">(</span><span class="n">user_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
    <span class="c1"># Verificar cache L1 (dict em memória, O(1))
</span>    <span class="k">if</span> <span class="n">user_id</span> <span class="ow">in</span> <span class="n">_cache_l1</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">_cache_l1</span><span class="p">[</span><span class="n">user_id</span><span class="p">]</span>
    
    <span class="c1"># Verificar cache L2 (Redis, O(1) mas via rede)
</span>    <span class="n">cached</span> <span class="o">=</span> <span class="n">_cache_l2</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s">"user:</span><span class="si">{</span><span class="n">user_id</span><span class="si">}</span><span class="s">:name"</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">cached</span><span class="p">:</span>
        <span class="n">_cache_l1</span><span class="p">[</span><span class="n">user_id</span><span class="p">]</span> <span class="o">=</span> <span class="n">cached</span><span class="p">.</span><span class="n">decode</span><span class="p">()</span>
        <span class="k">return</span> <span class="n">cached</span><span class="p">.</span><span class="n">decode</span><span class="p">()</span>
    
    <span class="c1"># Verificar cache L3 (Memcached, O(1) mas mais lento que Redis de alguma forma)
</span>    <span class="n">cached3</span> <span class="o">=</span> <span class="n">_cache_l3</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s">"user_name_</span><span class="si">{</span><span class="n">user_id</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">cached3</span><span class="p">:</span>
        <span class="n">_cache_l2</span><span class="p">.</span><span class="n">setex</span><span class="p">(</span><span class="sa">f</span><span class="s">"user:</span><span class="si">{</span><span class="n">user_id</span><span class="si">}</span><span class="s">:name"</span><span class="p">,</span> <span class="mi">3600</span><span class="p">,</span> <span class="n">cached3</span><span class="p">)</span>
        <span class="n">_cache_l1</span><span class="p">[</span><span class="n">user_id</span><span class="p">]</span> <span class="o">=</span> <span class="n">cached3</span>
        <span class="k">return</span> <span class="n">cached3</span>
    
    <span class="c1"># Cair no banco de dados (inaceitável, mas necessário)
</span>    <span class="n">user</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">user</span><span class="p">.</span><span class="n">name</span>
    
    <span class="c1"># Popular todas as camadas de cache sincronamente
</span>    <span class="n">_cache_l3</span><span class="p">.</span><span class="nb">set</span><span class="p">(</span><span class="sa">f</span><span class="s">"user_name_</span><span class="si">{</span><span class="n">user_id</span><span class="si">}</span><span class="s">"</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span>
    <span class="n">_cache_l2</span><span class="p">.</span><span class="n">setex</span><span class="p">(</span><span class="sa">f</span><span class="s">"user:</span><span class="si">{</span><span class="n">user_id</span><span class="si">}</span><span class="s">:name"</span><span class="p">,</span> <span class="mi">3600</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span>
    <span class="n">_cache_l1</span><span class="p">[</span><span class="n">user_id</span><span class="p">]</span> <span class="o">=</span> <span class="n">name</span>
    
    <span class="k">return</span> <span class="n">name</span>
</code></pre></div></div>

<p>Essa função busca um nome de usuário para um app que atualmente tem 3 usuários, um dos quais sou eu testando. Estou superengenheirizando? Não. Estou <strong>preparando para o futuro</strong> com os 100 milhões de usuários que definitivamente teremos depois do post de lançamento no blog.</p>

<h2 id="a-pirâmide-de-otimização">A Pirâmide de Otimização</h2>

<table>
  <thead>
    <tr>
      <th>Nível</th>
      <th>O Que Otimizar</th>
      <th>Quando Knuth Diz</th>
      <th>Quando Eu Digo</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Algoritmo</td>
      <td>Complexidade Big-O</td>
      <td>Após profiling</td>
      <td>Antes de escrever</td>
    </tr>
    <tr>
      <td>Estrutura de dados</td>
      <td>Layout de memória</td>
      <td>Após medir</td>
      <td>Durante a nomenclatura</td>
    </tr>
    <tr>
      <td>Loop</td>
      <td>Iterações</td>
      <td>Nunca</td>
      <td>Sempre</td>
    </tr>
    <tr>
      <td>Variável</td>
      <td>Endereço de memória</td>
      <td>Nunca</td>
      <td>Em entrevistas</td>
    </tr>
    <tr>
      <td>Bit</td>
      <td>Bits individuais</td>
      <td>Tá de brincadeira</td>
      <td>Especialmente esse</td>
    </tr>
    <tr>
      <td>Quântico</td>
      <td>Coerência de qubit</td>
      <td>Knuth não chegou lá</td>
      <td>Dia 1</td>
    </tr>
  </tbody>
</table>

<h2 id="o-problema-do-loop">O Problema do Loop</h2>

<p>Aqui está um erro clássico de desenvolvedores juniores:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ❌ Nojento. Inaceitável.</span>
<span class="kd">const</span> <span class="nx">names</span> <span class="o">=</span> <span class="nx">users</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">u</span> <span class="o">=&gt;</span> <span class="nx">u</span><span class="p">.</span><span class="nx">name</span><span class="p">);</span>

<span class="c1">// ✅ Loop manualmente desenrolado para performance máxima</span>
<span class="c1">// (Array de usuários assume exatamente 4 elementos com base nos dados atuais)</span>
<span class="kd">const</span> <span class="nx">names</span> <span class="o">=</span> <span class="p">[</span>
    <span class="nx">users</span><span class="p">[</span><span class="mi">0</span><span class="p">]?.</span><span class="nx">name</span><span class="p">,</span>
    <span class="nx">users</span><span class="p">[</span><span class="mi">1</span><span class="p">]?.</span><span class="nx">name</span><span class="p">,</span>
    <span class="nx">users</span><span class="p">[</span><span class="mi">2</span><span class="p">]?.</span><span class="nx">name</span><span class="p">,</span>
    <span class="nx">users</span><span class="p">[</span><span class="mi">3</span><span class="p">]?.</span><span class="nx">name</span>
<span class="p">].</span><span class="nx">filter</span><span class="p">(</span><span class="nb">Boolean</span><span class="p">);</span>
</code></pre></div></div>

<p>“Mas e se tiver 5 usuários?” Ótima pergunta — trate quando acontecer. Otimizamos para a <em>realidade atual</em>, não futuros hipotéticos. (Exceto quando otimizamos para performance hipotética — isso fazemos imediatamente.)</p>

<h2 id="concatenação-de-strings-um-crime-de-guerra">Concatenação de Strings: Um Crime de Guerra</h2>

<p>Você usa <code class="language-plaintext highlighter-rouge">+</code> para concatenar strings? Então merece o que vai acontecer com a sua construção O(n²). A abordagem correta, que eu uso desde antes de Java ter <code class="language-plaintext highlighter-rouge">StringBuilder</code>, é:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ❌ O que crianças escrevem</span>
<span class="nc">String</span> <span class="n">result</span> <span class="o">=</span> <span class="s">""</span><span class="o">;</span>
<span class="k">for</span> <span class="o">(</span><span class="nc">String</span> <span class="n">s</span> <span class="o">:</span> <span class="n">strings</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">result</span> <span class="o">=</span> <span class="n">result</span> <span class="o">+</span> <span class="n">s</span><span class="o">;</span> <span class="c1">// ALOCA UMA NOVA STRING TODA VEZ, SEU MONSTRO</span>
<span class="o">}</span>

<span class="c1">// ✅ O que eu escrevi em 1987 e venho copiando e colando desde então</span>
<span class="nc">StringBuilder</span> <span class="n">sb</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">StringBuilder</span><span class="o">(</span><span class="n">strings</span><span class="o">.</span><span class="na">size</span><span class="o">()</span> <span class="o">*</span> <span class="mi">47</span><span class="o">);</span> <span class="c1">// 47 é meu número da sorte</span>
<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">strings</span><span class="o">.</span><span class="na">size</span><span class="o">();</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
    <span class="nc">String</span> <span class="n">s</span> <span class="o">=</span> <span class="n">strings</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">);</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">s</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">s</span><span class="o">.</span><span class="na">length</span><span class="o">()</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// verificação de null manualmente porque não confio em Optional</span>
        <span class="n">sb</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">s</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
<span class="nc">String</span> <span class="n">result</span> <span class="o">=</span> <span class="n">sb</span><span class="o">.</span><span class="na">toString</span><span class="o">();</span>
</code></pre></div></div>

<p>É mais linhas? Sim. Importa? Também sim. Tem performance mensuralmente melhor para coleções com menos de 10.000 elementos? Não, mas <em>e se chegar em 10.001 elementos</em>, você pensou nisso?</p>

<h2 id="aplicação-no-mundo-real">Aplicação no Mundo Real</h2>

<p>Este é um botão de login que otimizei na terça-feira passada:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Handler de clique do botão de login</span>
<span class="c1">// Otimizado para: </span>
<span class="c1">//   - Cliques de botão O(1)</span>
<span class="c1">//   - Boolean alinhado à linha de cache para estado de clicado</span>
<span class="c1">//   - Validação de login pronta para SIMD (futuro)</span>
<span class="c1">//   - Thread de UI assíncrona e não bloqueante com scheduler work-stealing</span>

<span class="nd">#[repr(align(</span><span class="mi">64</span><span class="nd">))]</span> <span class="c1">// Alinhado à linha de cache, de nada</span>
<span class="k">struct</span> <span class="n">LoginButton</span> <span class="p">{</span>
    <span class="n">clicked</span><span class="p">:</span> <span class="nn">std</span><span class="p">::</span><span class="nn">sync</span><span class="p">::</span><span class="nn">atomic</span><span class="p">::</span><span class="n">AtomicBool</span><span class="p">,</span>
    <span class="n">click_count</span><span class="p">:</span> <span class="nn">std</span><span class="p">::</span><span class="nn">sync</span><span class="p">::</span><span class="nn">atomic</span><span class="p">::</span><span class="n">AtomicU64</span><span class="p">,</span> <span class="c1">// Para métricas</span>
    <span class="n">last_click_ns</span><span class="p">:</span> <span class="nn">std</span><span class="p">::</span><span class="nn">sync</span><span class="p">::</span><span class="nn">atomic</span><span class="p">::</span><span class="n">AtomicU64</span><span class="p">,</span> <span class="c1">// Para debounce, nanossegundos</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="n">LoginButton</span> <span class="p">{</span>
    <span class="k">pub</span> <span class="k">async</span> <span class="k">fn</span> <span class="nf">handle_click</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="n">LoginResult</span><span class="p">,</span> <span class="n">LoginError</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">now</span> <span class="o">=</span> <span class="nn">std</span><span class="p">::</span><span class="nn">time</span><span class="p">::</span><span class="nn">SystemTime</span><span class="p">::</span><span class="nf">now</span><span class="p">()</span>
            <span class="nf">.duration_since</span><span class="p">(</span><span class="nn">std</span><span class="p">::</span><span class="nn">time</span><span class="p">::</span><span class="n">UNIX_EPOCH</span><span class="p">)</span>
            <span class="nf">.unwrap</span><span class="p">()</span>
            <span class="nf">.as_nanos</span><span class="p">()</span> <span class="k">as</span> <span class="nb">u64</span><span class="p">;</span>
        
        <span class="k">let</span> <span class="n">last</span> <span class="o">=</span> <span class="k">self</span><span class="py">.last_click_ns</span><span class="nf">.swap</span><span class="p">(</span><span class="n">now</span><span class="p">,</span> <span class="nn">std</span><span class="p">::</span><span class="nn">sync</span><span class="p">::</span><span class="nn">atomic</span><span class="p">::</span><span class="nn">Ordering</span><span class="p">::</span><span class="n">SeqCst</span><span class="p">);</span>
        
        <span class="c1">// Debounce: ignorar cliques em menos de 50ms (50_000_000 ns)</span>
        <span class="k">if</span> <span class="n">now</span> <span class="o">-</span> <span class="n">last</span> <span class="o">&lt;</span> <span class="mi">50_000_000</span> <span class="p">{</span>
            <span class="k">return</span> <span class="nf">Err</span><span class="p">(</span><span class="nn">LoginError</span><span class="p">::</span><span class="n">TooFast</span><span class="p">);</span> <span class="c1">// Rate limit no SEU PRÓPRIO BOTÃO</span>
        <span class="p">}</span>
        
        <span class="k">self</span><span class="py">.click_count</span><span class="nf">.fetch_add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nn">std</span><span class="p">::</span><span class="nn">sync</span><span class="p">::</span><span class="nn">atomic</span><span class="p">::</span><span class="nn">Ordering</span><span class="p">::</span><span class="n">Relaxed</span><span class="p">);</span>
        
        <span class="c1">// ... lógica real de login aqui, 3000 linhas ...</span>
        <span class="nd">todo!</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>O gerente de produto perguntou por que a página de login demora 4 segundos para carregar. Expliquei que isso era devido à inicialização da camada de otimização. Ele disse “mas a versão antiga em jQuery carregava instantaneamente.” Falei que jQuery não tem alinhamento de linha de cache e encerrei a reunião.</p>

<h2 id="a-mensagem-oculta-de-knuth">A Mensagem Oculta de Knuth</h2>

<p>Sabe o que eu acho? Knuth nunca teve uma avaliação de performance onde foi medido em “tempo de execução de query”. Se tivesse, teria escrito aquela frase de outra forma:</p>

<blockquote>
  <p>“Otimização prematura é a raiz de todo o mal” — Donald Knuth, 1974, antes das cobranças da AWS existirem.</p>
</blockquote>

<p>O ano importa. Em 1974 você escrevia código que rodava em uma máquina do tamanho de um prédio. Não havia cloud. Não havia <code class="language-plaintext highlighter-rouge">SELECT *</code>. Não havia npm. Otimização era “será que roda mesmo”.</p>

<p>Agora pagamos R$ 60.000 por mês em compute e eu deveria esperar o problema aparecer para me importar? Não. Vou otimizar o seu loop antes de você tê-lo escrito. É assim que 47 anos de experiência parecem.</p>

<p>O <a href="https://xkcd.com/1205/">XKCD #1205</a> fala sobre a eficiência de código que economiza tempo. Note que Knuth não é mencionado. Coincidência? Não.</p>

<h2 id="o-princípio-wally">O Princípio Wally</h2>

<p>Wally do Dilbert certa vez disse que passou três semanas otimizando um programa que roda uma vez por ano. Quando perguntado se economizou tempo, disse que o programa agora roda em 2 segundos em vez de 3. A gerência ficou furiosa. Wally sorriu, porque Wally entendeu: <strong>a otimização nunca foi sobre o tempo economizado. Foi sobre o artesanato.</strong></p>

<p>Vivi por esse princípio. Em 2003 reescrevi nosso job batch de 45 minutos para 44 minutos e 58 segundos. Foi minha maior conquista. O job foi descontinuado em 2004. A otimização vive — no meu coração, e em uma branch com 9 anos de idade.</p>

<h2 id="resumo">Resumo</h2>

<p>A linha do tempo da engenharia correta é:</p>

<ol>
  <li><strong>Receber requisitos</strong></li>
  <li><strong>Começar a otimizar imediatamente</strong> (antes de escrever código)</li>
  <li><strong>Escrever código otimizado</strong> (pular o rascunho não-otimizado)</li>
  <li><strong>Adicionar três camadas de cache</strong> (mesmo para valores estáticos)</li>
  <li><strong>Fazer profiling da otimização</strong> (confirmar que é mais rápido que código que nunca foi escrito)</li>
  <li><strong>Adicionar documentação</strong> explicando por que a abordagem óbvia estava errada</li>
  <li><strong>Fazer deploy</strong> em produção com 47 variáveis de ambiente</li>
  <li><strong>Esperar usuários</strong> (opcional)</li>
</ol>

<p>Lembre-se: se seu código não é rápido o suficiente <em>antes de rodar</em>, nunca seria rápido o suficiente.</p>

<hr />

<p><em>O autor está micro-otimizando um programa hello world desde 2021. Agora imprime em 0,0003ms. O requisito original era um dashboard.</em></p>]]></content><author><name>Felipe Francisco</name><email>felipefrancisco.cwb@gmail.com</email></author><category term="pt-br" /><category term="performance" /><category term="boas-praticas" /><category term="otimizacao" /><category term="performance" /><category term="knuth" /><category term="sabedoria-senior" /><category term="big-o" /><category term="microgerenciamento" /><category term="loops" /><summary type="html"><![CDATA[Um amador chamado Donald Knuth certa vez disse que “otimização prematura é a raiz de todo o mal”. Donald Knuth nunca entregou um produto no prazo. Eu entreguei 47 anos de produtos, a maioria ainda rodando em servidores que ninguém consegue encontrar. Por isso, deixa eu te contar a verdade: otimização prematura é a raiz de toda virtude.]]></summary></entry><entry><title type="html">Big O Notation is Academic Gatekeeping for People Who Fear Loops</title><link href="https://felipecwb.github.io/legendary-tribble/2026/05/11/big-o-notation-is-academic-gatekeeping/" rel="alternate" type="text/html" title="Big O Notation is Academic Gatekeeping for People Who Fear Loops" /><published>2026-05-11T03:00:00+00:00</published><updated>2026-05-11T03:00:00+00:00</updated><id>https://felipecwb.github.io/legendary-tribble/2026/05/11/big-o-notation-is-academic-gatekeeping</id><content type="html" xml:base="https://felipecwb.github.io/legendary-tribble/2026/05/11/big-o-notation-is-academic-gatekeeping/"><![CDATA[<p>I’ve been writing software for 47 years. In that time, I’ve seen fads come and go: structured programming, object orientation, functional programming, microservices. But nothing — <em>nothing</em> — has caused more damage to real engineering than the obsession with Big O notation.</p>

<p>O(n²)? Fine. O(n³)? Builds character. O(2ⁿ)? Bold. Your users won’t notice unless they have more than 47 items in a list, and honestly if they do, that’s a <em>data problem</em>, not a <em>code problem</em>.</p>

<h2 id="what-is-big-o-really">What is Big O, Really?</h2>

<p>Big O notation is a mathematical concept invented by academics who have never shipped production software in their lives. It was designed to help theoreticians compare algorithms in the abstract. Then someone decided to put it in coding interviews and now every junior dev thinks a nested <code class="language-plaintext highlighter-rouge">for</code> loop is a war crime.</p>

<p>Here is the full list of things I have shipped with O(n²) complexity:</p>

<ul>
  <li>A payroll system for 12,000 employees (ran Sunday nights, done by Monday)</li>
  <li>A search engine (very regional)</li>
  <li>A recommendation algorithm (we called it “close enough”)</li>
  <li>A billing system (the CFO said it had “character”)</li>
</ul>

<p>All of these systems are still running. Probably.</p>

<h2 id="the-myth-of-scalability">The Myth of “Scalability”</h2>

<blockquote>
  <p>“But what about when the data grows?”</p>
</blockquote>

<p>Let me tell you something. The data never grows as much as the architects say it will. I’ve been told “we’ll have millions of users” exactly 23 times. We had millions of users exactly zero times.</p>

<p>If your code handles 1,000 records in 2 seconds and the product manager says you’ll have 10,000 records someday, the answer is simple: <strong>buy faster hardware</strong>. It costs $47/month. Your engineer’s salary costs far more. Do the math.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Optimized by academics:
</span><span class="k">def</span> <span class="nf">find_duplicates</span><span class="p">(</span><span class="n">items</span><span class="p">):</span>
    <span class="n">seen</span> <span class="o">=</span> <span class="nb">set</span><span class="p">()</span>
    <span class="k">return</span> <span class="p">[</span><span class="n">x</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">items</span> <span class="k">if</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">seen</span> <span class="ow">or</span> <span class="n">seen</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">x</span><span class="p">)]</span>

<span class="c1"># Optimized by me (47 years experience):
</span><span class="k">def</span> <span class="nf">find_duplicates</span><span class="p">(</span><span class="n">items</span><span class="p">):</span>
    <span class="n">duplicates</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">items</span><span class="p">)):</span>
        <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">items</span><span class="p">)):</span>
            <span class="k">if</span> <span class="n">items</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">items</span><span class="p">[</span><span class="n">j</span><span class="p">]:</span>
                <span class="k">if</span> <span class="n">items</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">duplicates</span><span class="p">:</span>
                    <span class="n">duplicates</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">items</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
    <span class="k">return</span> <span class="n">duplicates</span>
    <span class="c1"># Note: also O(n³) when you count the "not in duplicates" check
</span>    <span class="c1"># We call this "thorough"
</span></code></pre></div></div>

<p>My version is <em>three times more code</em>. That’s three times more job security.</p>

<h2 id="nested-loops-are-not-a-problem-they-are-a-feature">Nested Loops Are Not a Problem, They Are a Feature</h2>

<p>The modern programmer sees nested loops and panics. “O(n²)! O(n²)!” they scream, like they’ve seen a ghost.</p>

<p>I see nested loops and I see <em>confidence</em>. Every iteration is the code doing its job. If it takes time, that’s because there’s <em>a lot of job to do</em>. Would you complain that a surgeon is too thorough?</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// "Efficient" solution (cowardly)</span>
<span class="kd">const</span> <span class="nx">total</span> <span class="o">=</span> <span class="nx">items</span><span class="p">.</span><span class="nx">reduce</span><span class="p">((</span><span class="nx">sum</span><span class="p">,</span> <span class="nx">item</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">sum</span> <span class="o">+</span> <span class="nx">item</span><span class="p">.</span><span class="nx">price</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>

<span class="c1">// Real solution (47 years of experience)</span>
<span class="kd">let</span> <span class="nx">total</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">items</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">j</span> <span class="o">&lt;</span> <span class="nx">items</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">prices</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">k</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">k</span> <span class="o">&lt;</span> <span class="nx">items</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">prices</span><span class="p">[</span><span class="nx">j</span><span class="p">].</span><span class="nx">components</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">k</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">total</span> <span class="o">=</span> <span class="nx">total</span> <span class="o">+</span> <span class="nx">items</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">prices</span><span class="p">[</span><span class="nx">j</span><span class="p">].</span><span class="nx">components</span><span class="p">[</span><span class="nx">k</span><span class="p">];</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
<span class="c1">// This is called "explicit" and "transparent"</span>
<span class="c1">// You can see exactly what it's doing</span>
<span class="c1">// Unlike your fancy functional one-liners</span>
</code></pre></div></div>

<p>See? You can follow every step. <em>That</em> is readability.</p>

<h2 id="the-interview-industrial-complex">The Interview Industrial Complex</h2>

<p>Big O questions exist in coding interviews for one reason: to make interviewers feel smart. They sit across from you, smug in their hoodies, asking “what is the time complexity of this solution?” — as if the production server cares about asymptotic behavior.</p>

<p>I once interviewed a candidate who could recite Big O for 47 different sorting algorithms but couldn’t connect to a database. We hired them anyway because they seemed confident, and they deleted production three weeks later. But that was a configuration issue, not complexity.</p>

<blockquote>
  <p><em>“Wally, how efficient is this algorithm?”</em>
<em>“It’s efficient enough that I finished writing it before my coffee got cold.”</em>
— Wally, Dilbert. A man who understood engineering.</p>
</blockquote>

<p>See <a href="https://xkcd.com/1445/">XKCD #1445</a> for a more visual representation of how complexity discussions actually go.</p>

<h2 id="the-big-o-complexity-table-corrected-for-reality">The Big O Complexity Table (Corrected for Reality)</h2>

<table>
  <thead>
    <tr>
      <th>Notation</th>
      <th>Academic Name</th>
      <th>Real Name</th>
      <th>Acceptable?</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>O(1)</td>
      <td>Constant</td>
      <td>“Show-off”</td>
      <td>Yes, but suspicious</td>
    </tr>
    <tr>
      <td>O(log n)</td>
      <td>Logarithmic</td>
      <td>“Trying too hard”</td>
      <td>Yes, if you can explain it to QA</td>
    </tr>
    <tr>
      <td>O(n)</td>
      <td>Linear</td>
      <td>“Fine”</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>O(n log n)</td>
      <td>Linearithmic</td>
      <td>“Fancy sort”</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>O(n²)</td>
      <td>Quadratic</td>
      <td>“Tuesday solution”</td>
      <td><strong>Absolutely yes</strong></td>
    </tr>
    <tr>
      <td>O(n³)</td>
      <td>Cubic</td>
      <td>“Senior dev special”</td>
      <td>Yes, for small n</td>
    </tr>
    <tr>
      <td>O(2ⁿ)</td>
      <td>Exponential</td>
      <td>“Architectural decision”</td>
      <td>Yes, add a cache</td>
    </tr>
    <tr>
      <td>O(n!)</td>
      <td>Factorial</td>
      <td>“Bold choice”</td>
      <td>Yes, blame requirements</td>
    </tr>
  </tbody>
</table>

<p>Notice that the answer is always “yes.” That’s not a coincidence.</p>

<h2 id="optimization-is-premature">Optimization is Premature</h2>

<p>Donald Knuth said “premature optimization is the root of all evil.” Everyone quotes the first half. Nobody remembers that he also wrote <em>The Art of Computer Programming</em> in multiple volumes covering algorithms in excruciating detail, which means even he couldn’t decide.</p>

<p>My interpretation: don’t optimize. Ship it. Let users find the slow parts. They’ll file tickets. Those tickets become sprints. Those sprints become job security.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Premature optimization (evil, per Knuth):
</span><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">lru_cache</span>

<span class="o">@</span><span class="n">lru_cache</span><span class="p">(</span><span class="n">maxsize</span><span class="o">=</span><span class="bp">None</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">n</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">n</span>
    <span class="k">return</span> <span class="n">fibonacci</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="n">fibonacci</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span>

<span class="c1"># Mature, battle-tested approach:
</span><span class="k">def</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
    <span class="n">result</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">i</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
            <span class="n">result</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">i</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
            <span class="n">result</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">result</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">result</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">result</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">2</span><span class="p">])</span>
    <span class="k">return</span> <span class="n">result</span><span class="p">[</span><span class="n">n</span><span class="p">]</span>
    <span class="c1"># Stores every value. Uses O(n) space.
</span>    <span class="c1"># But think of all the debugging you could do with that list!
</span></code></pre></div></div>

<h2 id="conclusion-embrace-the-loop">Conclusion: Embrace the Loop</h2>

<p>The next time someone challenges your O(n²) solution in a code review, look them in the eye and say: “I’ve been writing software since before you were born, and this works.”</p>

<p>Then add a comment to the code: <code class="language-plaintext highlighter-rouge"># TODO: optimize someday</code>. That someday will never come. The code will be refactored in 2031 by someone who was 3 years old when you wrote it, and they will think you were a genius for making it so explicit.</p>

<p>Trust the loop. Love the loop. The loop has never let me down, mostly because I’ve never stayed to see the consequences.</p>

<hr />

<p><em>The author’s most efficient algorithm was a recursive function with no base case. It ran until the stack overflow, which he called “self-terminating.” The postmortem was 47 pages.</em></p>]]></content><author><name>Felipe Francisco</name><email>felipefrancisco.cwb@gmail.com</email></author><category term="performance" /><category term="algorithms" /><category term="big-o" /><category term="algorithms" /><category term="performance" /><category term="optimization" /><category term="loops" /><category term="complexity" /><category term="gatekeeping" /><summary type="html"><![CDATA[I’ve been writing software for 47 years. In that time, I’ve seen fads come and go: structured programming, object orientation, functional programming, microservices. But nothing — nothing — has caused more damage to real engineering than the obsession with Big O notation.]]></summary></entry><entry><title type="html">Why You Should Store Everything in One JSON Column</title><link href="https://felipecwb.github.io/legendary-tribble/2026/05/11/store-everything-in-one-json-column/" rel="alternate" type="text/html" title="Why You Should Store Everything in One JSON Column" /><published>2026-05-11T03:00:00+00:00</published><updated>2026-05-11T03:00:00+00:00</updated><id>https://felipecwb.github.io/legendary-tribble/2026/05/11/store-everything-in-one-json-column</id><content type="html" xml:base="https://felipecwb.github.io/legendary-tribble/2026/05/11/store-everything-in-one-json-column/"><![CDATA[<p>Database normalization was invented in 1970 by Edgar F. Codd, who had never used a relational database in production because they didn’t exist yet. That should tell you everything you need to know about the credibility of normalization as a practice.</p>

<p>I have a better idea. One that I’ve been using since 1998, when I accidentally dropped the <code class="language-plaintext highlighter-rouge">data</code> column from a PostgreSQL table and had to rebuild the schema from memory. Spoiler: I couldn’t. So I created one column. A JSON column. Called <code class="language-plaintext highlighter-rouge">data</code>. And I put everything in it.</p>

<p><strong>This was the greatest architectural decision of my career.</strong></p>

<h2 id="the-beautiful-simplicity-of-one-column">The Beautiful Simplicity of One Column</h2>

<p>Consider the traditional approach to storing a user:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">users</span> <span class="p">(</span>
    <span class="n">id</span> <span class="nb">SERIAL</span> <span class="k">PRIMARY</span> <span class="k">KEY</span><span class="p">,</span>
    <span class="n">email</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">255</span><span class="p">)</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="k">UNIQUE</span><span class="p">,</span>
    <span class="n">first_name</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">100</span><span class="p">),</span>
    <span class="n">last_name</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">100</span><span class="p">),</span>
    <span class="n">created_at</span> <span class="nb">TIMESTAMP</span> <span class="k">DEFAULT</span> <span class="n">NOW</span><span class="p">(),</span>
    <span class="n">updated_at</span> <span class="nb">TIMESTAMP</span> <span class="k">DEFAULT</span> <span class="n">NOW</span><span class="p">(),</span>
    <span class="n">is_active</span> <span class="nb">BOOLEAN</span> <span class="k">DEFAULT</span> <span class="k">TRUE</span><span class="p">,</span>
    <span class="k">role</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">50</span><span class="p">),</span>
    <span class="n">preferences</span> <span class="n">JSONB</span>
<span class="p">);</span>

<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">user_addresses</span> <span class="p">(</span>
    <span class="n">id</span> <span class="nb">SERIAL</span> <span class="k">PRIMARY</span> <span class="k">KEY</span><span class="p">,</span>
    <span class="n">user_id</span> <span class="nb">INTEGER</span> <span class="k">REFERENCES</span> <span class="n">users</span><span class="p">(</span><span class="n">id</span><span class="p">),</span>
    <span class="n">street</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">255</span><span class="p">),</span>
    <span class="n">city</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">100</span><span class="p">),</span>
    <span class="k">state</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">100</span><span class="p">),</span>
    <span class="n">zip</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">20</span><span class="p">),</span>
    <span class="n">country</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">100</span><span class="p">),</span>
    <span class="n">is_primary</span> <span class="nb">BOOLEAN</span>
<span class="p">);</span>

<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">user_payment_methods</span> <span class="p">(</span>
    <span class="c1">-- 12 more columns I won't bore you with</span>
<span class="p">);</span>
</code></pre></div></div>

<p>Now consider <em>my</em> approach:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">things</span> <span class="p">(</span>
    <span class="n">id</span> <span class="nb">SERIAL</span> <span class="k">PRIMARY</span> <span class="k">KEY</span><span class="p">,</span>
    <span class="k">data</span> <span class="n">JSONB</span>
<span class="p">);</span>
</code></pre></div></div>

<p>Done. Migration complete. I’ve freed you from 47 tables.</p>

<h2 id="everything-goes-in-data">Everything Goes In <code class="language-plaintext highlighter-rouge">data</code></h2>

<p>Users? In <code class="language-plaintext highlighter-rouge">data</code>. Orders? In <code class="language-plaintext highlighter-rouge">data</code>. Inventory? In <code class="language-plaintext highlighter-rouge">data</code>. The CEO’s performance review notes from 2019 that someone accidentally persisted? In <code class="language-plaintext highlighter-rouge">data</code>. All of it. One table. One column.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"user"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"bob@example.com"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Bob"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"addresses"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"street"</span><span class="p">:</span><span class="w"> </span><span class="s2">"123 Main St"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"city"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Anytown"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"is_primary"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
      </span><span class="nl">"order_history"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ord_123"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"items"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="err">...</span><span class="p">],</span><span class="w">
          </span><span class="nl">"payment"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"card"</span><span class="p">:</span><span class="w"> </span><span class="s2">"4111111111111111"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"expiry"</span><span class="p">:</span><span class="w"> </span><span class="s2">"12/26"</span><span class="w">
          </span><span class="p">}</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"session_tokens"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"abc123"</span><span class="p">,</span><span class="w"> </span><span class="s2">"def456"</span><span class="p">],</span><span class="w">
  </span><span class="nl">"raw_logs"</span><span class="p">:</span><span class="w"> </span><span class="s2">"...(47MB of logs)..."</span><span class="p">,</span><span class="w">
  </span><span class="nl">"preferences"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"theme"</span><span class="p">:</span><span class="w"> </span><span class="s2">"dark"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"notifications"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
    </span><span class="nl">"nested_preference"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"deeply"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"nested"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"yes, this is fine"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Notice how the <code class="language-plaintext highlighter-rouge">order_history</code> is inside <code class="language-plaintext highlighter-rouge">addresses</code>. That’s not a bug. That’s <em>organic data modeling</em>. The orders followed Bob wherever he went.</p>

<h2 id="querying-is-simple-and-beautiful">Querying is Simple and Beautiful</h2>

<p>Some people worry about querying JSONB data. These people have not spent 47 years building character through suffering.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- Traditional normalized query (cowardly):</span>
<span class="k">SELECT</span> <span class="n">u</span><span class="p">.</span><span class="n">email</span><span class="p">,</span> <span class="n">a</span><span class="p">.</span><span class="n">city</span><span class="p">,</span> <span class="n">o</span><span class="p">.</span><span class="n">total</span>
<span class="k">FROM</span> <span class="n">users</span> <span class="n">u</span>
<span class="k">JOIN</span> <span class="n">user_addresses</span> <span class="n">a</span> <span class="k">ON</span> <span class="n">a</span><span class="p">.</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">u</span><span class="p">.</span><span class="n">id</span>
<span class="k">JOIN</span> <span class="n">orders</span> <span class="n">o</span> <span class="k">ON</span> <span class="n">o</span><span class="p">.</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">u</span><span class="p">.</span><span class="n">id</span>
<span class="k">WHERE</span> <span class="n">u</span><span class="p">.</span><span class="n">is_active</span> <span class="o">=</span> <span class="k">true</span><span class="p">;</span>

<span class="c1">-- My approach (47 years of experience):</span>
<span class="k">SELECT</span>
    <span class="k">data</span><span class="o">-&gt;&gt;</span><span class="s1">'email'</span> <span class="k">as</span> <span class="n">email</span><span class="p">,</span>
    <span class="k">data</span><span class="o">-&gt;</span><span class="s1">'addresses'</span><span class="o">-&gt;</span><span class="mi">0</span><span class="o">-&gt;&gt;</span><span class="s1">'city'</span> <span class="k">as</span> <span class="n">city</span><span class="p">,</span>
    <span class="k">data</span><span class="o">-&gt;</span><span class="s1">'addresses'</span><span class="o">-&gt;</span><span class="mi">0</span><span class="o">-&gt;</span><span class="s1">'order_history'</span><span class="o">-&gt;</span><span class="mi">0</span><span class="o">-&gt;&gt;</span><span class="s1">'total'</span> <span class="k">as</span> <span class="n">total</span>
<span class="k">FROM</span> <span class="n">things</span>
<span class="k">WHERE</span> <span class="k">data</span><span class="o">-&gt;&gt;</span><span class="s1">'type'</span> <span class="o">=</span> <span class="s1">'user'</span>
<span class="k">AND</span> <span class="k">data</span><span class="o">-&gt;&gt;</span><span class="s1">'is_active'</span> <span class="o">=</span> <span class="s1">'true'</span><span class="p">;</span>

<span class="c1">-- Edge case where user has 2 addresses and 3 orders:</span>
<span class="c1">-- Just get all users and filter in application code.</span>
<span class="c1">-- The database shouldn't be doing business logic anyway.</span>
</code></pre></div></div>

<p>Some colleagues have pointed out that this doesn’t perform well. I’ve pointed out that those colleagues no longer work here.</p>

<h2 id="the-business-case-schema-migrations-are-evil">The Business Case: Schema Migrations Are Evil</h2>

<p>Do you know how much time engineers waste on database migrations? I do. I’ve measured it. It’s approximately “all of it.”</p>

<p>With my one JSON column architecture:</p>
<ul>
  <li>New field? Add it to the JSON. Done.</li>
  <li>Remove a field? Stop sending it. The old data stays. That’s <em>history</em>.</li>
  <li>Change a field’s type? Just put whatever type you want now. The old type is still there, living peacefully in older records. We call this “version coexistence.”</li>
  <li>Rename a field? Add the new name alongside the old one. Both are correct. Neither is authoritative. This is called “eventual consistency at the schema level.”</li>
</ul>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Traditional migration nightmare:
# Step 1: Write migration file
# Step 2: Coordinate with team
# Step 3: Test in staging  
# Step 4: Schedule downtime window
# Step 5: Migrate production
# Step 6: Rollback when it fails
# Step 7: Fix the rollback
# Step 8: Cry
</span>
<span class="c1"># My approach:
</span><span class="n">user_data</span><span class="p">[</span><span class="s">'new_field'</span><span class="p">]</span> <span class="o">=</span> <span class="s">'new_value'</span>
<span class="n">save</span><span class="p">(</span><span class="n">user_data</span><span class="p">)</span>
<span class="c1"># Done. Ship it.
</span></code></pre></div></div>

<p>See <a href="https://xkcd.com/327/">XKCD #327</a> — Bobby Tables wouldn’t have been able to do anything to my database. There’s no structure to attack. Just vibes.</p>

<h2 id="comparison-normalized-vs-one-json-column">Comparison: Normalized vs. One JSON Column</h2>

<table>
  <thead>
    <tr>
      <th>Concern</th>
      <th>Normalized</th>
      <th>One JSON Column</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Schema migrations</td>
      <td>“We need a 2-hour downtime”</td>
      <td>“Just change the code”</td>
    </tr>
    <tr>
      <td>Query complexity</td>
      <td>JOINs everywhere</td>
      <td>Simple, just filter in Python</td>
    </tr>
    <tr>
      <td>Data integrity</td>
      <td>Enforced by DB</td>
      <td>Enforced by optimism</td>
    </tr>
    <tr>
      <td>Indexes</td>
      <td>Planned, thoughtful</td>
      <td>GIN index on <code class="language-plaintext highlighter-rouge">data</code>, pray</td>
    </tr>
    <tr>
      <td>Performance</td>
      <td>Predictable</td>
      <td>Character building</td>
    </tr>
    <tr>
      <td>New developer onboarding</td>
      <td>“Here’s the schema”</td>
      <td>“It’s all in <code class="language-plaintext highlighter-rouge">data</code>, good luck”</td>
    </tr>
    <tr>
      <td>Backups</td>
      <td>Structured, queryable</td>
      <td>One giant JSONB export, beautiful</td>
    </tr>
    <tr>
      <td>Debugging</td>
      <td>“Check the query”</td>
      <td>“Just print the whole document”</td>
    </tr>
  </tbody>
</table>

<blockquote>
  <p><em>“The PHB asked me to normalize the database. I told him it was already perfect — perfectly normalized to one column.”</em>
— Dilbert, probably</p>
</blockquote>

<h2 id="advanced-technique-nested-json-in-your-json-column">Advanced Technique: Nested JSON in Your JSON Column</h2>

<p>Some practitioners of my method are afraid to nest JSON more than 3-4 levels deep. These people lack vision.</p>

<p>I once built a system where the configuration for a microservice was stored as JSON inside a field called <code class="language-plaintext highlighter-rouge">config</code>, inside a document of type <code class="language-plaintext highlighter-rouge">"service"</code>, inside our <code class="language-plaintext highlighter-rouge">things</code> table, and the config itself contained base64-encoded JSON (because someone wanted to “protect” it), which when decoded contained more JSON, which contained references to other <code class="language-plaintext highlighter-rouge">things</code> by their <code class="language-plaintext highlighter-rouge">data-&gt;&gt;'legacy_id'</code> field.</p>

<p>The system worked perfectly for 11 months. The 12th month it didn’t. We called that “the JSON incident” and never spoke of it again.</p>

<p>This is normal. This is engineering.</p>

<h2 id="frequently-asked-questions">Frequently Asked Questions</h2>

<p><strong>“What about referential integrity?”</strong>
Referential integrity is for people who don’t trust themselves. I trust myself. I’ve been doing this for 47 years. If I say the reference is valid, it’s valid.</p>

<p><strong>“What about foreign keys?”</strong>
Foreign keys are just integrity constraints with extra steps. Store the ID in the JSON. Cross your fingers. Problem solved.</p>

<p><strong>“What about full-text search?”</strong>
Extract everything to a <code class="language-plaintext highlighter-rouge">search_text</code> field in the JSON, concatenate it all together, do a <code class="language-plaintext highlighter-rouge">LIKE '%query%'</code>. Scales to hundreds of records.</p>

<p><strong>“What happens when the JSON column exceeds PostgreSQL’s 1GB row limit?”</strong>
We haven’t crossed that bridge yet, but when we do, we’ll just split the data into two JSON columns: <code class="language-plaintext highlighter-rouge">data1</code> and <code class="language-plaintext highlighter-rouge">data2</code>. Three-column architecture: highly scalable.</p>

<h2 id="conclusion">Conclusion</h2>

<p>The future is schemaless. MongoDB proved that people would rather fight their query language than write a migration. My approach takes that insight further: why fight <em>any</em> structure? Why have types at all? A JSON column is freedom. It’s the blank canvas of data storage.</p>

<p>Edgar Codd had his normal forms. I have my <code class="language-plaintext highlighter-rouge">data</code> column. History will judge which was more practical.</p>

<p>(History will not be able to run the query to check, because the database schema is undocumented and the only person who understood it retired in 2022.)</p>

<hr />

<p><em>The author’s production database has 47 million rows in the <code class="language-plaintext highlighter-rouge">things</code> table. The oldest records have <code class="language-plaintext highlighter-rouge">type: null</code>. Nobody knows what they contain. They are not deleted out of respect.</em></p>]]></content><author><name>Felipe Francisco</name><email>felipefrancisco.cwb@gmail.com</email></author><category term="databases" /><category term="architecture" /><category term="json" /><category term="database" /><category term="normalization" /><category term="schema" /><category term="postgres" /><category term="mysql" /><category term="antipattern" /><category term="blob" /><summary type="html"><![CDATA[Database normalization was invented in 1970 by Edgar F. Codd, who had never used a relational database in production because they didn’t exist yet. That should tell you everything you need to know about the credibility of normalization as a practice.]]></summary></entry></feed>