<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Adam - The Developer</title>
    <description>The latest articles on DEV Community by Adam - The Developer (@adamthedeveloper).</description>
    <link>https://dev.to/adamthedeveloper</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1002243%2F84fa5f44-c4e1-4fec-934c-9fa687161e10.webp</url>
      <title>DEV Community: Adam - The Developer</title>
      <link>https://dev.to/adamthedeveloper</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adamthedeveloper"/>
    <language>en</language>
    <item>
      <title>When Duplicate Code Is the Better Design</title>
      <dc:creator>Adam - The Developer</dc:creator>
      <pubDate>Mon, 01 Jun 2026 06:09:57 +0000</pubDate>
      <link>https://dev.to/adamthedeveloper/when-duplicate-code-is-the-better-design-1idk</link>
      <guid>https://dev.to/adamthedeveloper/when-duplicate-code-is-the-better-design-1idk</guid>
      <description>&lt;p&gt;You've seen the developer. Maybe you &lt;em&gt;are&lt;/em&gt; the developer.&lt;/p&gt;

&lt;p&gt;They discover DRY — ✨ &lt;strong&gt;Don't Repeat Yourself&lt;/strong&gt; ✨ — and something switches in their brain. A primal need awakens. Every duplicated string, every similar-looking function, every pair of lines that rhyme in the wrong light becomes a &lt;strong&gt;personal affront&lt;/strong&gt;. An itch. A moral failing.&lt;/p&gt;

&lt;p&gt;Two weeks later, their codebase looks like a game of Jenga where every piece is also load-bearing, also abstract, also parametrized six ways to Sunday, and also, crucially, &lt;strong&gt;completely impossible to understand&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Congratulations. You've achieved ✨ DRY ✨. You've also achieved a codebase that will ruin your next three Fridays and everyone's.&lt;/p&gt;




&lt;h2&gt;
  
  
  What DRY Actually Says (and Doesn't)
&lt;/h2&gt;

&lt;p&gt;DRY comes from &lt;em&gt;The Pragmatic Programmer&lt;/em&gt; by Andy Hunt and Dave Thomas. The actual rule is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Every piece of &lt;strong&gt;knowledge&lt;/strong&gt; must have a single, unambiguous, authoritative representation within a system."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Notice what it says: &lt;strong&gt;knowledge&lt;/strong&gt;. it's not &lt;em&gt;code&lt;/em&gt;. it's not &lt;em&gt;characters&lt;/em&gt;. it's not &lt;em&gt;"things that look similar when squinted at from across the room."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It says knowledge.&lt;/p&gt;

&lt;p&gt;The principle is about avoiding duplicated &lt;em&gt;intent and logic&lt;/em&gt; — not scrubbing every repeated character like you're laundering evidence. But somewhere between the book and the keyboard, people stopped reading and started pattern-matching like raccoons sorting shiny garbage:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"If two lines of code look alike, merge them immediately or you're morally bankrupt, professionally suspect, and probably the reason standups run long."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's not DRY. That's &lt;strong&gt;aesthetic OCD with a philosophy degree and a GitHub contribution graph to protect&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Taxonomy of DRY Crimes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Crime #1: Abstracting Coincidental Similarity
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Two functions that happen to look the same TODAY&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;formatUserName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;formatAuthorName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The classic DRY-brain response: &lt;em&gt;"These are identical! Extract!"&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The abstraction&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;formatName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fine. Harmless, even. Like a campfire in dry grass. Six months later, user names want a salutation, author names want a pen name fallback, and your cute little &lt;code&gt;formatName&lt;/code&gt; has metastasized into a fifteen-parameter horror show with an enum called &lt;code&gt;NameFormattingStrategy&lt;/code&gt; — the kind of function that needs its own onboarding doc and makes junior devs reconsider their career choices.&lt;/p&gt;

&lt;p&gt;The duplication wasn't a bug. It was &lt;strong&gt;two different things that happened to share a shape for one Tuesday&lt;/strong&gt;. A user is not an author. Their names evolve on different timelines. The duplicate code was whispering that — you heard "refactor opportunity" and built a cage instead.&lt;/p&gt;




&lt;h3&gt;
  
  
  Crime #2: The Abstraction That Needs a Manual
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before: readable in 3 seconds&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getActiveAdminUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isActive&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// After: DRY'd into a puzzle box&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;filterEntities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;predicates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sortBy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transform&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filtered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;predicates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;every&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pred&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;pred&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sorted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sortBy&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;filtered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sortBy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;filtered&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;limited&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;transform&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;limited&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;limited&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Usage (good luck, new hire)&lt;/span&gt;
&lt;span class="nf"&gt;filterEntities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isActive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You saved four lines. You created a &lt;code&gt;filterEntities&lt;/code&gt; function that now silently accumulates every filtering need in your entire app, grows to 200 lines, and gets passed to new developers with the haunted look of someone handing off a cursed object.&lt;/p&gt;

&lt;p&gt;The original function had a &lt;strong&gt;name&lt;/strong&gt;. It was easy to understand, it told a simple story, &lt;code&gt;getActiveAdminUsers&lt;/code&gt; is self-documenting. Your generalized thing is a puzzle.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Code is read far more than it's written. Abstractions are not free — they are paid for in comprehension, every single time someone opens that file.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Crime #3: DRY Across Wrong Boundaries
&lt;/h3&gt;

&lt;p&gt;The most insidious form. And unlike the toy examples above, this one has a body count.&lt;/p&gt;

&lt;p&gt;Every backend dev has seen this happen. You're building a notification system. You have email, SMS, push, and in-app notifications. They all take a user, a type, and some options. They look identical at the call site:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// v1 — reasonable. clean. you feel good about yourself.&lt;/span&gt;
&lt;span class="nf"&gt;sendNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order_confirmed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;orderId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The abstraction makes sense. You write one function. You route internally. Ship it.&lt;/p&gt;

&lt;p&gt;Then marketing wants to send a promotional email blast. Different thing, same function — but now you need an &lt;code&gt;isMarketing&lt;/code&gt; flag because marketing emails have unsubscribe footers and transactional ones don't.&lt;/p&gt;

&lt;p&gt;Then legal needs CAN-SPAM compliance on marketing sends. Different opt-out logic per locale. Now you need &lt;code&gt;locale&lt;/code&gt; and &lt;code&gt;complianceRules&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then mobile push notifications need to respect iOS quiet hours, which email doesn't care about. Add &lt;code&gt;respectQuietHours&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then in-app notifications don't go through any external service at all — they're just a database write — but they're still "notifications" so they stay in the function.&lt;/p&gt;

&lt;p&gt;Then SMS gets a character limit and needs message chunking logic that email will never need.&lt;/p&gt;

&lt;p&gt;Eighteen months after that clean &lt;code&gt;sendNotification(user, type, options)&lt;/code&gt;, you have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// v∞ — the function that consumed itself&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sendNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NotificationType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NotificationOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sms&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;push&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;in_app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;isMarketing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;isTransactional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;complianceRules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComplianceConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;respectQuietHours&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;chunkIfOverLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;bypassOptOut&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// "just this once, for the launch"&lt;/span&gt;
  &lt;span class="nx"&gt;trackingPixel&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;channel&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isMarketing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// 40 lines of compliance logic&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// 30 lines of transactional logic&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;channel&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sms&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 50 lines that have nothing to do with email&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;channel&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;push&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 45 lines that have nothing to do with SMS&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;channel&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;in_app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// just writes to a table but must pass through this gauntlet anyway&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function is now 300 lines. It has no tests that actually cover all the flag combinations — there are 2⁷ of them, good luck. Every engineer who touches it adds one parameter at the top and one &lt;code&gt;if&lt;/code&gt; branch somewhere in the middle, and prays they didn't break the Malaysian SMS path.&lt;/p&gt;

&lt;p&gt;You have not DRY'd your code. You have built a &lt;strong&gt;load-bearing monolith disguised as a helper function&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The actual fix is humbling: email, SMS, push, and in-app were never the same thing. They shared a surface-level interface and nothing else. They have different rate limits, different compliance regimes, different retry logic, different failure modes, and different delivery guarantees. The right design was always:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;sendSms&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;sendPushNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;createInAppNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, it's four functions. Yes, some options are repeated across them. That's fine — the repetition is &lt;em&gt;honest&lt;/em&gt;. Each function can evolve independently. The SMS team can add chunking without touching the email function. Legal can update the CAN-SPAM logic without a regression on push. A new engineer can read &lt;code&gt;sendEmail&lt;/code&gt; and understand &lt;code&gt;sendEmail&lt;/code&gt; without holding the entire notification universe in their head.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The shared abstraction didn't eliminate complexity. It hid it, then grew it, then held it hostage.&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Crime #4: The Generic Repository That Ate Your Domain
&lt;/h3&gt;

&lt;p&gt;Every backend dev hits this phase. You have &lt;code&gt;UserService&lt;/code&gt;, &lt;code&gt;OrderService&lt;/code&gt;, &lt;code&gt;ProductService&lt;/code&gt;. They all need to fetch things from the database. The queries look suspiciously similar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Three services, three nearly identical queries. Your DRY alarm is screaming.&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;findAllUsers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;deletedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;findAllOrders&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;not&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;draft&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;findAllProducts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;isPublished&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix writes itself. You extract a base class. You add generics. You feel like you're finally doing &lt;em&gt;real&lt;/em&gt; architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The abstraction that will haunt three teams&lt;/span&gt;
&lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseRepository&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;filters&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;findPaginated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;filters&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseRepository&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderRepository&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseRepository&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;orders&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductRepository&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseRepository&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Beautiful. Reusable. You deleted forty lines and posted about it in Slack. Life is good.&lt;/p&gt;

&lt;p&gt;Then product management asks for order search with customer name, date range, and payment status — but only for admins, and only orders that haven't been refunded unless the refund was partial. Your &lt;code&gt;findPaginated&lt;/code&gt; now accepts a &lt;code&gt;QueryOptions&lt;/code&gt; object with twelve optional fields and a &lt;code&gt;include&lt;/code&gt; array that half the team misconfigures.&lt;/p&gt;

&lt;p&gt;Then users need soft-delete scoping, role-based visibility, and a "last active" sort that requires a join. You override &lt;code&gt;findAll&lt;/code&gt; in &lt;code&gt;UserRepository&lt;/code&gt;. Then you override &lt;code&gt;findPaginated&lt;/code&gt; too. The base class is now mostly dead code that new hires still have to read.&lt;/p&gt;

&lt;p&gt;Then products need full-text search, category trees, and inventory counts from a warehouse table in another service. Someone adds &lt;code&gt;findPaginatedWithSearch&lt;/code&gt;. Someone else adds &lt;code&gt;findPaginatedWithJoins&lt;/code&gt;. The base class grows a &lt;code&gt;buildQuery&lt;/code&gt; hook, then a &lt;code&gt;QueryBuilder&lt;/code&gt; parameter, then an escape hatch called &lt;code&gt;rawQueryOverride&lt;/code&gt; that three repositories use and nobody documents.&lt;/p&gt;

&lt;p&gt;Eighteen months later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// v∞ — BaseRepository, but make it suffer&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;findPaginated&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Entity&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;FilterMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;include&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;IncludeMap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;joins&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;JoinConfig&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="nl"&gt;search&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;SearchConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;SortConfig&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;SortConfig&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="nl"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;internal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;softDelete&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;bypassTenantScope&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// "temporary, for the migration"&lt;/span&gt;
    &lt;span class="nl"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;AggregateConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;row&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PaginatedResult&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;TransformedRow&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 180 lines of conditional query assembly&lt;/span&gt;
  &lt;span class="c1"&gt;// every repository passes a different options shape&lt;/span&gt;
  &lt;span class="c1"&gt;// the type signature is longer than most functions it replaces&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You have not eliminated duplication. You have built a &lt;strong&gt;generic pagination framework&lt;/strong&gt; that every entity in your system must awkwardly fit into, like forcing every piece of furniture through the same IKEA allen wrench.&lt;/p&gt;

&lt;p&gt;The honest version was always:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// UserRepository — boring, explicit, correct&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;findActiveUsersForAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// OrderRepository — different domain, different query&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;findOrdersForDashboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrderDashboardFilters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ProductRepository — nobody else's problem&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;findPublishedProductsWithInventory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;categoryId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, they all paginate. Yes, they all query a database. That's &lt;strong&gt;infrastructure similarity&lt;/strong&gt;, not &lt;strong&gt;domain knowledge&lt;/strong&gt;. Users, orders, and products don't share a reason to change — they share a SQL dialect. Conflating those two is how you end up with a &lt;code&gt;BaseEntityManagerFactoryHelperUtil&lt;/code&gt; and a Jira ticket titled "refactor findPaginated (blocked: needs architect approval)."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pagination is not a domain concept. It's a transport detail. Your repository layer is not a place to build a query DSL because SELECT statements rhymed once.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Principle You Actually Need: AHA
&lt;/h2&gt;

&lt;p&gt;Sandi Metz — of &lt;em&gt;Practical Object-Oriented Design&lt;/em&gt; fame — offers the antidote:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AHA: Avoid Hasty Abstractions.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The rule: &lt;strong&gt;prefer duplication over the wrong abstraction&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A wrong abstraction is worse than duplication because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Duplication is visible and easy to fix&lt;/li&gt;
&lt;li&gt;A wrong abstraction is load-bearing, hard to see, and expensive to undo&lt;/li&gt;
&lt;li&gt;People are afraid to delete abstractions, so they pile parameters on top until it collapses&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The heuristic she and others suggest: &lt;strong&gt;wait for the third time&lt;/strong&gt;. Once — write it. Twice — note the repetition. Three times — &lt;em&gt;now&lt;/em&gt; think about what abstraction, if any, makes sense. By then you have enough examples to see the &lt;em&gt;actual&lt;/em&gt; shape of the knowledge, not just the accidental shape of the code.&lt;/p&gt;




&lt;h2&gt;
  
  
  When DRY Is Right
&lt;/h2&gt;

&lt;p&gt;To be fair to the principle: DRY is genuinely critical in the right places.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Business rules.&lt;/strong&gt; The formula for calculating interest, the rule for what makes a user "active," the threshold for triggering an alert — these must live in exactly one place. When the business logic changes, you want to change exactly one thing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configuration.&lt;/strong&gt; A base URL hardcoded in twelve files is twelve places a typo can happen. That's rightfully DRY.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data schemas.&lt;/strong&gt; Your database schema and your validation schema should not diverge because you copy-pasted them. Derive one from the other.&lt;/p&gt;

&lt;p&gt;The test for whether something &lt;em&gt;should&lt;/em&gt; be DRY: &lt;strong&gt;if this thing needs to change, how many places need to change with it, and do those places share a reason to change?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If yes — DRY it.&lt;/p&gt;

&lt;p&gt;If they just look similar today — leave it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Anti-Pattern DRY Is Trying to Fight
&lt;/h2&gt;

&lt;p&gt;DRY's real enemy was never "duplicate code." It was &lt;strong&gt;duplicate knowledge&lt;/strong&gt; — the same business rule, scattered across the system, slowly drifting out of sync.&lt;/p&gt;

&lt;p&gt;The nightmare scenario: your pricing logic is in the database trigger, the API controller, the frontend calculation, and a comment in a Slack message from 2019. When pricing changes, you find three of them. The fourth one quietly disagrees for six months, occasionally giving users the wrong number, and you never know why.&lt;/p&gt;

&lt;p&gt;That's what DRY is for. Not for making your &lt;code&gt;formatName&lt;/code&gt; function 40% more reusable.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Checklist for Before You Abstract
&lt;/h2&gt;

&lt;p&gt;Before you extract that abstraction, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Is this duplicate &lt;em&gt;knowledge&lt;/em&gt;, or duplicate &lt;em&gt;shape&lt;/em&gt;?&lt;/strong&gt; Two things can look alike for different reasons.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Can I name this abstraction clearly?&lt;/strong&gt; If you're reaching for &lt;code&gt;processEntityData&lt;/code&gt; or &lt;code&gt;handleThing&lt;/code&gt;, stop. The abstraction isn't real yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What happens when one usage changes?&lt;/strong&gt; If you can't change the abstraction without worrying about the other usages, it's too coupled.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Am I doing this because it's right, or because repetition makes me uncomfortable?&lt;/strong&gt; Valid question. The answer matters.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;DRY is a heuristic, not a commandment. It was written to fight a specific enemy: knowledge duplication causing systems to rot. It was not written to make your codebase a shrine to abstraction.&lt;/p&gt;

&lt;p&gt;The best codebases I've read had a little repetition in them. Deliberate repetition. The kind where someone clearly decided "these two things look alike but they're not the same thing, and I'm going to leave them separate so they can evolve separately."&lt;/p&gt;

&lt;p&gt;That's not laziness. That's wisdom.&lt;/p&gt;

&lt;p&gt;Write the thing twice if you have to. Your future self, reading the code at 11pm with no context, will thank you for the clarity.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>programming</category>
      <category>productivity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Minimal Code Doesn’t Mean Stable Code</title>
      <dc:creator>Adam - The Developer</dc:creator>
      <pubDate>Tue, 26 May 2026 04:15:08 +0000</pubDate>
      <link>https://dev.to/adamthedeveloper/minimal-code-doesnt-mean-stable-code-4mbd</link>
      <guid>https://dev.to/adamthedeveloper/minimal-code-doesnt-mean-stable-code-4mbd</guid>
      <description>&lt;p&gt;The argument sounds reasonable: fewer lines of code mean fewer bugs. Simpler to review, easier to reason about, less surface area for defects. Sounds great. It's true. But it's also incomplete.&lt;/p&gt;

&lt;p&gt;The problem starts when backend developers treat production systems like homework assignments. In a single-process app:&lt;/p&gt;

&lt;p&gt;you control execution. You know the order. Threads might race, but at least they share the same memory and clock. &lt;/p&gt;

&lt;p&gt;Once you have APIs talking to databases, webhooks firing at midnight, async jobs on a queue, and three replicas behind a load balancer, the failure modes multiply: connections drop, messages arrive out of order, clocks disagree, and partial failures show up at 3 AM on Tuesdays. &lt;/p&gt;

&lt;p&gt;Trimming code doesn't make any of that go away. It just hides the complexity until something breaks.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Minimal Code Meets Production
&lt;/h2&gt;

&lt;p&gt;Consider what happens when your minimalist masterpiece meets reality:&lt;/p&gt;

&lt;p&gt;Your service temporarily loses connection to the database for 30 seconds. Your code has no timeout logic. Requests hang. Users refresh. More requests queue up. Eventually something breaks.&lt;/p&gt;

&lt;p&gt;Two instances process the same webhook because you thought "that probably won't happen." No idempotency key, so the charge runs twice. Your balance sheet now has an extra $50,000 in it. Your accountant is confused. Your manager is less confused.&lt;/p&gt;

&lt;p&gt;A worker crashes mid-operation. There's no recovery mechanism. The transaction is abandoned in an inconsistent state. Your data is now in a state that violates every assumption you made about how it should look.&lt;/p&gt;

&lt;p&gt;A retry storm after a downstream blip hammers your API because nothing backs off or deduplicates. Rate limits trip. Legitimate traffic gets dropped. You're debugging an outage caused by code that "handled errors" by logging and returning.&lt;/p&gt;

&lt;p&gt;None of these are prevented by writing less. They're prevented by writing the boring safeguards you skipped because they looked redundant.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Production Actually Requires
&lt;/h2&gt;

&lt;p&gt;Modern backend systems need safeguards that simple applications never had to think about:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Idempotency.&lt;/strong&gt; Every operation must be safe to retry. A payment webhook redelivered, a queue message processed twice, a client that retries on timeout—all of these need a way to recognize "already done." Operation IDs, version numbers, dedupe keys. Not glamorous. Required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Timeouts.&lt;/strong&gt; Requests to other services need deadlines. Without them, cascading failures happen silently and gradually consume all your resources. Your code will just sit there, waiting, like a phone call that never connects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compensation Logic.&lt;/strong&gt; When a multi-step operation fails partway through, something has to undo the work already committed. You can't abandon a half-finished saga and hope nobody notices. That's more code than assuming success. People skip it anyway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conflict Detection.&lt;/strong&gt; When two writers touch the same record—two API instances, a retry overlapping with the original request—you need version checks, timestamps, or optimistic locking. Pretending conflicts don't exist works until two updates land in the wrong order.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observability.&lt;/strong&gt; Logging, metrics, and traces that let you reconstruct what happened when something fails. At 3 AM, you'll wish this existed. When something breaks and you have no logs, you'll understand why this matters.&lt;/p&gt;

&lt;p&gt;You can't delete these and call it simplification. You're just moving complexity from your editor into your on-call rotation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Less Code vs. Less Noise
&lt;/h2&gt;

&lt;p&gt;Kill redundant abstractions, dead logic, and speculative frameworks. That's good discipline.&lt;/p&gt;

&lt;p&gt;Deleting retry wrappers, validation, circuit breakers, or idempotency checks because they "add noise" is a different move. &lt;/p&gt;

&lt;p&gt;You're betting stability on dependencies you don't control. When the database hiccups, the partner API times out, or Kubernetes reschedules a pod mid-request, the system doesn't get simpler. It gets wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Test
&lt;/h2&gt;

&lt;p&gt;If your app runs more than one instance, talks to other services, or processes work asynchronously, these questions will eventually matter:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If a process dies mid-operation, can the system detect it and recover correctly?&lt;/li&gt;
&lt;li&gt;If a message is delayed several seconds, what actually happens?&lt;/li&gt;
&lt;li&gt;If two workers attempt the same operation at once, is the result deterministic or a coin flip?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you can't answer all three with specific mechanisms—not vibes, not "we'll fix it in prod"—the codebase isn't simple. It's fragile.&lt;/p&gt;

&lt;p&gt;Write the safeguards. Handle the failure modes. The goal isn't more lines for their own sake; it's making hidden complexity visible before production does it for you.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>backend</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>The Mid-Dev Paradox: Why Juniors Are Chill and Mid-Levels Will Reorganize Your Code at 3 AM</title>
      <dc:creator>Adam - The Developer</dc:creator>
      <pubDate>Wed, 20 May 2026 09:58:51 +0000</pubDate>
      <link>https://dev.to/adamthedeveloper/the-mid-dev-paradox-why-juniors-are-chill-and-mid-levels-will-reorganize-your-code-at-3-am-2bh4</link>
      <guid>https://dev.to/adamthedeveloper/the-mid-dev-paradox-why-juniors-are-chill-and-mid-levels-will-reorganize-your-code-at-3-am-2bh4</guid>
      <description>&lt;p&gt;Here's a thing nobody talks about in the "building a healthy engineering culture" TED talks: &lt;strong&gt;junior developers are not the problem&lt;/strong&gt;. They're predictable. They ask questions. They listen. They know what they don't know, which is half the battle.&lt;/p&gt;

&lt;p&gt;The mid-level developer? Now &lt;em&gt;there's&lt;/em&gt; your chaos agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tier List (Controversial, I Know)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Juniors:&lt;/strong&gt; "I don't understand this. Can you explain it?"&lt;br&gt;
&lt;strong&gt;Translation:&lt;/strong&gt; &lt;em&gt;easy, good faith problem&lt;/em&gt;. You explain it. They learn. Maybe they code it a weird way, but at least they'll tell you before shipping. These people are trying not to sink the boat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mids:&lt;/strong&gt; "Yeah, I read a Medium article about this architecture pattern once."&lt;br&gt;
&lt;strong&gt;Translation:&lt;/strong&gt; &lt;em&gt;oh no&lt;/em&gt;. They now know juuuust enough to be confident, not enough to see all the edges they're missing. They're about to refactor your entire codebase because TypeScript stricter settings are "table stakes for any serious project" (they read an article).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Seniors:&lt;/strong&gt; "We could do it that way, but let's think about what breaks in six months."&lt;br&gt;
&lt;strong&gt;Translation:&lt;/strong&gt; &lt;em&gt;calm, because we've seen it break in six months before&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Juniors Are Actually Great
&lt;/h2&gt;

&lt;p&gt;A junior asked why the onboarding docs were three years out of date. We laughed. Then realized: none of us had updated them because we all just knew how things worked now. New person had to learn from Slack messages and asking people questions. We'd optimized ourselves into a bottleneck.&lt;/p&gt;

&lt;p&gt;Here's the thing about juniors: they haven't been beaten down yet. They still think documentation should exist. They still think things should be findable. Revolutionary concepts, I know.&lt;/p&gt;

&lt;p&gt;You point them at a task, they get stuck, they ask where the docs are, and you realize there &lt;em&gt;aren't&lt;/em&gt; any. Or they're scattered across four different Slack threads from 2021. They don't know yet that this is normal. They think "onboarding" means something other than "asking random people questions until you figure it out."&lt;/p&gt;

&lt;p&gt;The risk with a junior is they'll point out all the gaps and make you feel bad about it. The upside is they're collaborative about fixing it. They're not trying to prove anything. They're just trying to do the job and not feel lost the whole time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Mid-Dev Energy Crisis
&lt;/h2&gt;

&lt;p&gt;Mids are where things get spicy.&lt;/p&gt;

&lt;p&gt;They've shipped something. Maybe it worked, maybe it kind of worked, maybe it caught fire but they're not thinking about that right now. The point is: they have &lt;em&gt;success weight&lt;/em&gt;. They're no longer a question mark—they're a contributor with opinions.&lt;/p&gt;

&lt;p&gt;But here's where it gets fun. They've read enough to be dangerous, not enough to be wise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Mid-Dev's Greatest Hits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Architecture Reorganization:&lt;/strong&gt; "I've been thinking... what if we moved everything to microservices? Distributed systems are the future. I watched a talk." No strategic reason. No pain point being solved. They just watched a talk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Framework Rewrite:&lt;/strong&gt; "Okay but what if we rewrote the whole thing in [new framework]?" Why? "Better DX." Whose DX? Their DX for writing new code while everyone else owns production incidents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Premature Abstraction:&lt;/strong&gt; Four months into the project, they've created a custom hook ecosystem so complex that only they understand it. Junior tries to use it, asks for help, gets a 40-minute explanation of prop-drilling alternatives nobody asked for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Aggressive Code Review:&lt;/strong&gt; They're reviewing a junior's PR. The code works. It's readable. It passes tests. They leave 12 comments asking why the developer "didn't consider using a custom hook" or "didn't memoize aggressively" or "didn't think about server rendering" even though it's a form in an admin panel that hits 30 users a month.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The mid-dev is learning that they can have opinions, and they're exercising that power like they just got access to a 2002 Honda Civic and need to test the engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Happens (And It's Not Their Fault)
&lt;/h2&gt;

&lt;p&gt;Here's the uncomfortable truth: mids are in the danger zone of confidence versus competence. They're past the "I don't know anything" phase but not yet in the "I know what I don't know" phase.&lt;/p&gt;

&lt;p&gt;The Dunning-Kruger effect is real, but it's not really about stupidity. It's about &lt;em&gt;scale&lt;/em&gt;. A junior's worked on one thing. A mid's worked on three to five things. That &lt;em&gt;feels&lt;/em&gt; like a lot. It &lt;em&gt;feels&lt;/em&gt; like they've seen the patterns.&lt;/p&gt;

&lt;p&gt;Until they haven't. Until they learn (the hard way) that "best practice" depends on context. That premature optimization isn't just bad—it's actively hostile to shipping. That code that's boring and works beats code that's clever and breaks at 2 AM.&lt;/p&gt;

&lt;p&gt;The thing is, every senior developer alive was a mid once. Most of us pushed something completely unnecessary to production while congratulating ourselves on our architecture skills. Most of us spent a week optimizing something that didn't matter. That's not a failure—that's training.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Works
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;If you're a senior managing a mid:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Don't tell them they're wrong—that just makes them defensive. Instead, ask them questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"What would break if we do this?" (Often: silence, then they realize something.)&lt;/li&gt;
&lt;li&gt;"How long will the refactor take? What's the payoff?" (This makes them do math. Math is humbling.)&lt;/li&gt;
&lt;li&gt;"Can we ship the simple version first and refactor if we hit actual problems?" (This teaches patience.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Give them ownership of something that can break &lt;em&gt;a little bit&lt;/em&gt; without destroying the business. Let them experience consequences without trauma. This is how you turn a mid into a senior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you're a mid reading this:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Look, you're doing great. You're learning fast, you're getting stronger, you're building real things. The fact that you're in this uncomfortable middle zone means you're &lt;em&gt;growing&lt;/em&gt;. That's good.&lt;/p&gt;

&lt;p&gt;But here's the move: &lt;strong&gt;start asking seniors why instead of telling juniors what&lt;/strong&gt;. When you're about to suggest a rewrite, ask a senior if they've seen that problem before. When you're reviewing code, ask yourself if you're protecting the system or protecting your ego.&lt;/p&gt;

&lt;p&gt;The seniors you respect aren't the ones with the cleverest solutions. They're the ones who know when simple is better. You can get there faster than you think, but only if you stay curious instead of confident.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Insight
&lt;/h2&gt;

&lt;p&gt;Juniors are dangerous in ways we expect—they might not write optimal code, they might miss edge cases. We've built systems to catch that (code review, testing, etc). We &lt;em&gt;expect&lt;/em&gt; juniors to be junior.&lt;/p&gt;

&lt;p&gt;Mids are dangerous in ways we don't expect. They're good enough that we trust their judgment. They're confident enough that they don't ask for second opinions. And they've read just enough Medium articles to be genuinely convinced they're right.&lt;/p&gt;

&lt;p&gt;The thing that separates a mid from a senior isn't skill—it's humility. It's knowing that "I don't know" doesn't mean you're not good at this; it means you're paying attention.&lt;/p&gt;

&lt;p&gt;So yeah, keep your juniors close. Let them ask questions. Appreciate that they still think code should be boring and work-y instead of clever and break-y.&lt;/p&gt;

&lt;p&gt;And the mids? Keep them curious. Give them hard problems. Have them explain their solutions to someone who wasn't part of the decision. Let them fail small.&lt;/p&gt;

&lt;p&gt;The goal is to make juniors into mids and mids into people who remember what it felt like to be certain about everything.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>programming</category>
      <category>career</category>
      <category>architecture</category>
    </item>
    <item>
      <title>⚔️ Go vs Java: The Minimalist vs The Enterprise Veteran</title>
      <dc:creator>Adam - The Developer</dc:creator>
      <pubDate>Mon, 11 May 2026 06:12:47 +0000</pubDate>
      <link>https://dev.to/adamthedeveloper/go-vs-java-the-minimalist-vs-the-enterprise-veteran-1gg3</link>
      <guid>https://dev.to/adamthedeveloper/go-vs-java-the-minimalist-vs-the-enterprise-veteran-1gg3</guid>
      <description>&lt;p&gt;&lt;strong&gt;No sides. No agenda. Just two languages walking into a bar and us watching what happens.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The Setup&lt;/li&gt;
&lt;li&gt;The Contenders&lt;/li&gt;
&lt;li&gt;A Quick Origin Story&lt;/li&gt;
&lt;li&gt;Language Philosophy: Complexity vs. Simplicity&lt;/li&gt;
&lt;li&gt;Performance: JVM vs Native Binary&lt;/li&gt;
&lt;li&gt;Concurrency: Goroutines vs Virtual Threads&lt;/li&gt;
&lt;li&gt;Ecosystem &amp;amp; Libraries: The Forest vs The Toolshed&lt;/li&gt;
&lt;li&gt;Tooling: Go's Discipline vs Java's Buffet&lt;/li&gt;
&lt;li&gt;
Team Learning Curve: The First Month Matters

&lt;ul&gt;
&lt;li&gt;Go: Steep and Short&lt;/li&gt;
&lt;li&gt;Java: Gradual and Endless&lt;/li&gt;
&lt;li&gt;The Team Implication&lt;/li&gt;
&lt;li&gt;In Practice&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Where Each One Shines

&lt;ul&gt;
&lt;li&gt;Go is great for:&lt;/li&gt;
&lt;li&gt;Java is great for:&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The Honest Answer&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚔️  The Setup
&lt;/h2&gt;

&lt;p&gt;I decided to write this because I've been drowning in content where people compare languages like they're picking a religion. "This is better than that, that is better than this," and they're mostly just optimized for outrage.&lt;/p&gt;

&lt;p&gt;I've been writing Java for a long time. Fell in love with it early—the way it handles threads, the concurrency model, the entire ecosystem around it just &lt;em&gt;clicked&lt;/em&gt;. For a while I genuinely thought Java was the language where threading finally became practical and not a complete nightmare.&lt;/p&gt;

&lt;p&gt;Full transparency: I also wanted to write Java because back then, knowing Java meant programmers would praise you at parties. There was definitely ego in it. (Okay, there was &lt;em&gt;a lot&lt;/em&gt; of ego in it.) But the reasons I still reach for it now are actually technical and only &lt;em&gt;somewhat&lt;/em&gt; ego-driven.&lt;/p&gt;

&lt;p&gt;Then Go came along. Started writing it about two years ago and I've been enjoying it. Built two distributed systems at work with it. But it makes you &lt;em&gt;do&lt;/em&gt; things manually, the syntax feels weirdly alien the first time you see it, and the ecosystem has this vibe of "I hope this library doesn't get abandoned next month."&lt;/p&gt;

&lt;p&gt;No hate on Go. I genuinely love using it.&lt;/p&gt;

&lt;p&gt;So here we are—I'm writing this (technically during a very boring class about a week ago) to actually break down what these languages are good at, so you can make a smarter choice than "everyone's using X so we should too."&lt;/p&gt;




&lt;h2&gt;
  
  
  🥊 The Contenders
&lt;/h2&gt;

&lt;p&gt;Two languages dominate a lot of backend conversations right now. One has been around since the disco era, survived the dot-com bust, and somehow became the backbone of half the world's enterprise software. The other was built by Google engineers who were tired of waiting for C++ to compile, and it became the quiet workhorse behind Docker, Kubernetes, and half of modern infrastructure.&lt;/p&gt;

&lt;p&gt;Java and Go. The veteran and the minimalist. The cathedral and the toolshed.&lt;/p&gt;

&lt;p&gt;Neither is objectively better. Both are genuinely excellent at different things. This post isn't here to crown a winner. It's here to help you understand what each one is &lt;em&gt;actually good at&lt;/em&gt;, so you can make a smarter call the next time someone says "so what stack are we using?"&lt;/p&gt;

&lt;p&gt;Let's get into it.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏁 A Quick Origin Story
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Java&lt;/strong&gt; was born in 1995 at Sun Microsystems, led by &lt;strong&gt;James Gosling&lt;/strong&gt;, with one promise: &lt;em&gt;Write Once, Run Anywhere&lt;/em&gt;. The JVM meant your compiled bytecode could run on any machine. This was revolutionary at the time. Java rode that wave into enterprise dominance and never really left.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Go&lt;/strong&gt; (or Golang) was created at Google in 2009 by &lt;strong&gt;Rob Pike&lt;/strong&gt;, &lt;strong&gt;Ken Thompson&lt;/strong&gt;, and &lt;strong&gt;Robert Griesemer&lt;/strong&gt;. These three people have more programming language credentials than most of us will ever accumulate. Their frustration? C++ build times were destroying their productivity. Their solution? A language that was fast to compile, fast to run, and simple enough that you couldn't shoot yourself in the foot too badly.&lt;/p&gt;

&lt;p&gt;Different eras. Different problems. Different philosophies.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Language Philosophy: Complexity vs. Simplicity
&lt;/h2&gt;

&lt;p&gt;This is where the two languages diverge most dramatically. Not in syntax, but in &lt;em&gt;worldview&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Java&lt;/strong&gt; believes in giving you tools. Lots of tools. Generics, inheritance, abstract classes, interfaces, annotations, lambdas, streams, optional, records, sealed classes. Java has a solution for every pattern, and then a pattern for every solution. It trusts you to assemble them wisely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Go&lt;/strong&gt; believes in taking tools away. Go has no classes (just structs). No inheritance. No generics until recently (Go 1.18, 2022). No exceptions, just error values returned explicitly. The Go team's philosophy is almost aggressively minimalist. If a feature could be abused, they'd rather not include it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Go error handling: explicit, everywhere, always&lt;/span&gt;
&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"data.txt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to open file: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Java: exceptions handle the unhappy path separately&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FileReader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"data.txt"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// do stuff&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RuntimeException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to open file"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neither approach is wrong. Java's exception model keeps the happy path clean. Go's explicit errors mean you &lt;em&gt;cannot&lt;/em&gt; forget to handle failure. The compiler won't let you ignore an error value without being deliberate about it.&lt;/p&gt;

&lt;p&gt;Go's philosophy produces code that a new team member can read on day one. Java's philosophy produces code that can model genuinely complex domains with precision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dig deeper if:&lt;/strong&gt; You care about this. &lt;a href="https://go.dev/doc/faq" rel="noopener noreferrer"&gt;Go's design FAQ&lt;/a&gt; and &lt;a href="https://openjdk.org/projects/amber/" rel="noopener noreferrer"&gt;Java's language evolution&lt;/a&gt; tell very different stories about how languages grow.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚡ Performance: JVM vs Native Binary
&lt;/h2&gt;

&lt;p&gt;Go compiles to a &lt;strong&gt;native binary&lt;/strong&gt;. You run &lt;code&gt;go build&lt;/code&gt;, you get a self-contained executable that starts in milliseconds. It's like handing someone a knife—they just use it.&lt;/p&gt;

&lt;p&gt;Java runs on the &lt;strong&gt;JVM&lt;/strong&gt;, which is more like handing someone a full kitchen. There's setup time (the JVM initializes), there's a warm-up period where the JIT compiler figures out what code you're running a lot (and starts optimizing it), but once it knows what's happening, it can produce machine code that's genuinely competitive or sometimes better than Go for sustained workloads.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Go&lt;/th&gt;
&lt;th&gt;Java&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cold start&lt;/td&gt;
&lt;td&gt;🟢 Milliseconds&lt;/td&gt;
&lt;td&gt;🟡 Seconds (improving with GraalVM)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Peak throughput (warmed up)&lt;/td&gt;
&lt;td&gt;🟡 Very fast&lt;/td&gt;
&lt;td&gt;🟢 Can match or beat Go&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory footprint&lt;/td&gt;
&lt;td&gt;🟢 Small&lt;/td&gt;
&lt;td&gt;🟡 Larger baseline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serverless / short-lived processes&lt;/td&gt;
&lt;td&gt;🟢 Natural fit&lt;/td&gt;
&lt;td&gt;🟡 JVM overhead hurts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long-running services&lt;/td&gt;
&lt;td&gt;🟡 Great&lt;/td&gt;
&lt;td&gt;🟢 JIT optimization pays off&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The tradeoff:&lt;/strong&gt; Go's predictable, instant startup is perfect for environments where things are constantly spinning up and down. Java's startup cost disappears if the process lives for weeks—the JIT warmup happens once, and then you get increasingly optimized code.&lt;/p&gt;

&lt;p&gt;GraalVM native images exist if you want Java's ecosystem with Go's startup speed, but you're adding complexity to your build. It's a bridge, not a solution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dig deeper if:&lt;/strong&gt; &lt;a href="https://www.techempower.com/benchmarks/" rel="noopener noreferrer"&gt;TechEmpower benchmarks&lt;/a&gt; if you like staring at numbers.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔀 Concurrency: Goroutines vs Virtual Threads
&lt;/h2&gt;

&lt;p&gt;Go's concurrency story used to be the unreachable dream for everyone else. &lt;strong&gt;Goroutines&lt;/strong&gt; are lightweight, greenthread-style concurrency that the Go runtime manages for you. You can spawn tens of thousands without breaking a sweat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Launch 10,000 concurrent tasks. No ceremony.&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="n"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;doSomethingBlocking&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Channels are your communication layer—they're the part that makes goroutines actually &lt;em&gt;elegant&lt;/em&gt; instead of just fast:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="s"&gt;"hello from another goroutine"&lt;/span&gt;
&lt;span class="p"&gt;}()&lt;/span&gt;

&lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This mental model (goroutines + channels) became foundational to Go. It made high-concurrency systems feel operationally approachable. That's why Docker, Kubernetes, Prometheus—all the infrastructure that had to handle millions of goroutines—are written in Go.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Java had a problem here.&lt;/strong&gt; For years, the answer to "how do I handle thousands of concurrent requests?" was "spawn a thread per request" or "use a thread pool and hope." It worked, but it didn't &lt;em&gt;feel&lt;/em&gt; right. You could feel the language fighting you.&lt;/p&gt;

&lt;p&gt;Then Java 21 brought &lt;strong&gt;Virtual Threads&lt;/strong&gt;. Same idea as goroutines—lightweight, JVM-managed concurrency. But here's the thing: they look exactly like regular Java threads. No new syntax, no new mental model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Java 21: 100,000 virtual threads. Same old executor API.&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;IntStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doSomethingBlocking&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The real difference:&lt;/strong&gt; Go requires you to think in a new way. Goroutines and channels are a genuinely elegant paradigm, but they're different from how most languages do concurrency. Java's virtual threads let you keep thinking the old way—submit work, forget about it, let the runtime handle the threads.&lt;/p&gt;

&lt;p&gt;Go's approach produces more elegant concurrency code when you're building from scratch. Java's approach is pragmatic when you have existing blocking code or when you don't want to learn a new concurrency philosophy just to handle concurrent requests.&lt;/p&gt;

&lt;p&gt;Both solve the same problem. Go solved it first and more elegantly. Java solved it later and more "you don't have to change anything."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dig deeper if:&lt;/strong&gt; &lt;a href="https://go.dev/blog/goroutines-and-channels" rel="noopener noreferrer"&gt;The Go Blog on goroutines&lt;/a&gt; or &lt;a href="https://openjdk.org/jeps/444" rel="noopener noreferrer"&gt;JEP 444: Virtual Threads&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌲 Ecosystem &amp;amp; Libraries: The Forest vs The Toolshed
&lt;/h2&gt;

&lt;p&gt;Java's ecosystem is &lt;em&gt;vast&lt;/em&gt;. I'm talking millions of artifacts in Maven Central. Whatever you need exists somewhere. Database drivers, HTTP clients, payment processors, ML frameworks—multiple mature options, probably more than you want to choose from. The Spring ecosystem alone is essentially its own platform. Spring Boot, Spring Data, Spring Cloud, Spring Security. Teams build entire careers knowing just that one thing deeply.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The tradeoff:&lt;/strong&gt; Abundance creates paralysis. You're choosing between 47 JSON libraries and second-guessing yourself. A "simple" Spring Boot project pulls in hundreds of transitive dependencies. You're managing a forest, and sometimes you can't see the trees.&lt;/p&gt;

&lt;p&gt;Go's ecosystem is younger and more curated. The standard library is &lt;em&gt;actually good&lt;/em&gt;—HTTP servers, JSON encoding, crypto, testing are all production-quality and baked in. The community has filled in the gaps with solid packages: &lt;code&gt;gin&lt;/code&gt;, &lt;code&gt;echo&lt;/code&gt;, &lt;code&gt;gorm&lt;/code&gt;, &lt;code&gt;cobra&lt;/code&gt;. But sometimes you hit the edge. A niche domain where nothing exists, and now you're writing it yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The tradeoff:&lt;/strong&gt; You make fewer decisions, have fewer dependencies to worry about, and your binaries are smaller. But occasionally you're building something the ecosystem hasn't solved yet.&lt;/p&gt;

&lt;p&gt;Here's where it matters: For &lt;strong&gt;small, bounded services&lt;/strong&gt; (webhooks, rate limiters, health checkers, internal tools), Go's minimal approach keeps things clean and understandable. You grab the standard library, maybe add one focused package, and you're done. For &lt;strong&gt;complex enterprise systems&lt;/strong&gt; (multi-tenant SaaS with user roles, audit trails, compliance logging, payment integration), Java's ecosystem saves you months of building. Spring Data handles the database complexity that would be a pain to build. Spring Security handles authentication scenarios that would take forever to get right.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Go: 8 lines, no dependencies&lt;/span&gt;
&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/health"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusOK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintln&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;`{"status": "ok"}`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Spring: more setup, but it's assuming you'll build an actual system on top&lt;/span&gt;
&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HealthController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/health"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;health&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Go version is simpler when you actually need simplicity. The Spring version pays dividends when complexity is inevitable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dig deeper if:&lt;/strong&gt; &lt;a href="https://pkg.go.dev" rel="noopener noreferrer"&gt;pkg.go.dev&lt;/a&gt; or &lt;a href="https://mvnrepository.com" rel="noopener noreferrer"&gt;mvnrepository.com&lt;/a&gt; (warning: you will feel overwhelmed).&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Tooling: Go's Discipline vs Java's Buffet
&lt;/h2&gt;

&lt;p&gt;Go ships with an &lt;em&gt;opinionated&lt;/em&gt; standard toolchain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;go fmt&lt;/code&gt;: formats your code. Non-negotiable. Everyone's Go code looks the same.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;go test&lt;/code&gt;: testing built in, no framework needed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;go vet&lt;/code&gt;: catches common mistakes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;go mod&lt;/code&gt;: dependency management, built in since Go 1.11&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;go build&lt;/code&gt;: one command, one binary&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are no arguments about Go tooling. It's just &lt;em&gt;there&lt;/em&gt;, it works, and the whole Go community uses the same tools.&lt;/p&gt;

&lt;p&gt;Java's tooling is more of a choose-your-own-adventure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build tools: Maven or Gradle (religious war ongoing since 2012)&lt;/li&gt;
&lt;li&gt;Testing: JUnit + Mockito + AssertJ + maybe Testcontainers + maybe Spock&lt;/li&gt;
&lt;li&gt;Formatting: Checkstyle? Google Java Format? Your lead's personal preferences from 2015?&lt;/li&gt;
&lt;li&gt;Dependency management: Maven Central or JitPack or that internal Nexus your company runs that nobody fully understands&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Java tooling ecosystem is powerful, flexible, and the source of at least 30% of new developer onboarding time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The tradeoff:&lt;/strong&gt; Go's rigid tooling means less time arguing about style and setup, but also less flexibility if you have unusual needs. Java's flexible tooling means you &lt;em&gt;can&lt;/em&gt; optimize for your exact situation, but you have to make more decisions upfront.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dig deeper if:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go tooling: &lt;code&gt;go help&lt;/code&gt; in your terminal is genuinely great. &lt;/li&gt;
&lt;li&gt;Java: the &lt;a href="https://maven.apache.org/guides/" rel="noopener noreferrer"&gt;Maven docs&lt;/a&gt; or &lt;a href="https://docs.gradle.org" rel="noopener noreferrer"&gt;Gradle docs&lt;/a&gt; depending on which side of history you're on.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  👥 Team Learning Curve: The First Month Matters
&lt;/h2&gt;

&lt;p&gt;This is where language choice gets &lt;em&gt;practical&lt;/em&gt; in ways that benchmarks completely miss.&lt;/p&gt;

&lt;h3&gt;
  
  
  Go: Steep and Short
&lt;/h3&gt;

&lt;p&gt;Week one with Go is rough. The syntax looks wrong to you: &lt;code&gt;defer&lt;/code&gt;, &lt;code&gt;goroutines&lt;/code&gt;, &lt;code&gt;channels&lt;/code&gt;, &lt;code&gt;interfaces without explicit implementation&lt;/code&gt;. You'll write code that compiles but doesn't feel right. You'll stare at a pointer receiver and wonder why it exists.&lt;/p&gt;

&lt;p&gt;But then something shifts. By week three, you're productive. There's just not enough to learn. Go is &lt;em&gt;intentionally simple&lt;/em&gt;—it has fewer corners, fewer patterns, fewer ways to paint yourself into a corner. You get to the end of the learning curve faster because there &lt;em&gt;is&lt;/em&gt; an end.&lt;/p&gt;

&lt;p&gt;After a month, you can pick up any Go codebase and understand it. The style is consistent because &lt;code&gt;gofmt&lt;/code&gt; is non-negotiable. There's usually one way to do things, so debates are settled by the language itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Java: Gradual and Endless
&lt;/h3&gt;

&lt;p&gt;A new Java developer is productive &lt;em&gt;fast&lt;/em&gt;. Spring Boot handles so much boilerplate. IntelliJ is powerful enough that you can write working code without really knowing what you're doing. By week one, you've shipped something.&lt;/p&gt;

&lt;p&gt;But productive ≠ competent. The learning curve doesn't end, it just gets less steep.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generics and wildcards: "What's a &lt;code&gt;? super T&lt;/code&gt;?"&lt;/li&gt;
&lt;li&gt;Inheritance hierarchies: "Why does this class extend AbstractSomething which implements Interface-Whatever?"&lt;/li&gt;
&lt;li&gt;Dependency injection: "How did this bean get instantiated?"&lt;/li&gt;
&lt;li&gt;Streams vs for loops: "Which should I use?"&lt;/li&gt;
&lt;li&gt;Checked vs unchecked exceptions: "Should I throw this or declare it?"&lt;/li&gt;
&lt;li&gt;Annotations: "Is this magic or is it explicit?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After a month, you ship features. But they're not idiomatic. You copy patterns without understanding them. You build things the complex way because Java &lt;em&gt;can&lt;/em&gt; do complexity, so you assume it &lt;em&gt;should&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;After 6 months, you start thinking in Java. After a year, you're actually dangerous.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Team Implication
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Go teams scale horizontally.&lt;/strong&gt; Hire three junior developers, and by week four they're all contributing meaningfully. Code reviews are fast because there's less to argue about. The language enforces consistency. New people can't accidentally introduce wildly different patterns because the language doesn't allow them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Java teams scale with depth.&lt;/strong&gt; Hire three senior engineers who know Spring inside-out, and they can architect complex systems. But hire three mid-level developers, and you'll spend months establishing patterns. The payoff is that once you have that shared understanding, you can build systems that would be awkward in Go.&lt;/p&gt;

&lt;h3&gt;
  
  
  In Practice
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Go:&lt;/strong&gt; New developer → valuable by day 3 → production-ready code by day 20&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Java:&lt;/strong&gt; New developer → visible output by day 5 → stops making seniors cringe by day 90&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your team is mostly junior and turns over frequently, Go reduces friction. People ramp up before they leave. If your team is senior and stable, Java's richness becomes an asset. You can mentor through the complexity, and the codebase can express sophisticated requirements.&lt;/p&gt;

&lt;p&gt;Neither is better. They're different onboarding curves with different endpoints.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏢 Where Each One Shines
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Go is great for:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cloud-native infrastructure&lt;/strong&gt;: Docker, Kubernetes, Terraform, Prometheus—all Go. They needed to handle the concurrency problem (goroutines managing millions of containers), and Go's lightweight concurrency made high-scale infrastructure feel operationally approachable. Few mainstream languages made this density practical at the time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Microservices and APIs&lt;/strong&gt;: Small binary, fast startup, low memory. When you're deploying dozens of services to containers that spin up and down constantly, Go's millisecond startup matters operationally. The JVM's seconds are a constant background friction in that scenario.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CLI tools&lt;/strong&gt;: Single binary, no runtime, just works. Ship a Go executable to users and they run it. That's it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Network-heavy services&lt;/strong&gt;: Goroutines handle tens of thousands of concurrent connections efficiently. If you're building something that lives at the edge (proxy, load balancer, API gateway), this becomes an operational advantage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Teams with high turnover or a strong consistency preference&lt;/strong&gt;: The language enforces one way of doing things. New people ramp fast. Debates about style disappear because &lt;code&gt;gofmt&lt;/code&gt; is non-negotiable.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Java is great for:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complex enterprise domains&lt;/strong&gt;: The type system (generics, sealed classes, records) lets you model intricate business logic precisely. When requirements change three years later, the compiler helps you find everything that needs updating. Java makes you be explicit about contracts, and that pays off over time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;High-throughput, long-running services&lt;/strong&gt;: Once the JVM warms up—which happens once, then stays warm for weeks—the JIT produces increasingly optimized code. In services running continuously and handling millions of requests, this optimization compounds. You get better performance the longer it runs, the opposite of microservices.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Large, stable teams&lt;/strong&gt;: Onboarding takes longer, but once your team knows the patterns, Java's explicitness becomes a feature. You can architect complex systems and have everyone understand them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data-heavy applications&lt;/strong&gt;: Hibernate, Spring Data, the JPA ecosystem—they've solved a lot of hard database problems. Complex queries, transactions, migrations, relationship management. Go's database story works, but Java's is more mature and battle-tested.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Existing Java investment&lt;/strong&gt;: You have code that works, people who know it, and switching costs are real. Modern Java (21+) is genuinely better to work with than it used to be. Virtual threads solved a real weakness. Stay and improve rather than rewrite.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Systems that need long-term evolution&lt;/strong&gt;: Java's type system helps you reason about changes years later. The language pushes you toward being explicit about constraints and contracts. That discipline pays off when requirements get complex.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🤷 The Honest Answer
&lt;/h2&gt;

&lt;p&gt;Pick Go if you're building infrastructure tooling, CLIs, or containerized microservices where startup time and memory footprint actually matter operationally. Pick it if you want to move fast without debating style guides. Pick it if your team is junior and you don't have time to babysit learning curves.&lt;/p&gt;

&lt;p&gt;Pick Java if you're building something genuinely complex and you need a type system that grows with your requirements. Pick it if you already have Java in your organization and switching would be a nightmare. Pick it if your team is senior and stable—the language rewards expertise and knowledge accumulation.&lt;/p&gt;

&lt;p&gt;The honest truth is that the language is rarely the bottleneck. Architecture, database design, team communication, deployment infrastructure—those matter more. But choosing the right tool for your constraints does save you from fighting friction that shouldn't exist.&lt;/p&gt;

&lt;p&gt;Both are genuinely good at what they're designed for. You're not picking between "good" and "bad." You're picking between "good for this" and "good for that."&lt;/p&gt;

</description>
      <category>programming</category>
      <category>java</category>
      <category>go</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Write Code That's Easy to Delete: The Art of Impermanent Software</title>
      <dc:creator>Adam - The Developer</dc:creator>
      <pubDate>Sat, 02 May 2026 08:22:42 +0000</pubDate>
      <link>https://dev.to/adamthedeveloper/write-code-thats-easy-to-delete-the-art-of-impermanent-software-19l1</link>
      <guid>https://dev.to/adamthedeveloper/write-code-thats-easy-to-delete-the-art-of-impermanent-software-19l1</guid>
      <description>&lt;p&gt;&lt;em&gt;We obsess over making code last. Maybe we should obsess over making it leave gracefully.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;There's a quote that's been living rent-free in my head for years:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Write code that is easy to delete, not easy to extend."&lt;/strong&gt;&lt;br&gt;
— Tef, &lt;em&gt;programming is terrible&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first time I read it, I pushed back. Isn't the whole point to write code that &lt;em&gt;survives&lt;/em&gt;? That scales? That you can build on top of?&lt;/p&gt;

&lt;p&gt;Then I spent a weekend trying to rip out a logging library from a three-year-old codebase. It had quietly spread into 40 files. Removing it felt like surgery on a patient who had grown bones around a sponge.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Lie We Tell Ourselves
&lt;/h2&gt;

&lt;p&gt;When we write code, we tell ourselves a flattering story: &lt;em&gt;this will be here in five years, so I should make it robust, reusable, and extensible.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But the data doesn't support this story. Most features get changed within months. Many get cut entirely. The average production codebase has entire directories that haven't been touched in years — not because they're perfect, but because everyone is too afraid to delete them.&lt;/p&gt;

&lt;p&gt;We write code as if it's load-bearing. Usually, it isn't.&lt;/p&gt;

&lt;p&gt;The irony is that the more we try to make code "permanent" — wrapping it in abstractions, coupling it into shared utilities, weaving it through the system, the &lt;em&gt;harder&lt;/em&gt; it becomes to change. We've traded adaptability for the illusion of durability.&lt;/p&gt;




&lt;h2&gt;
  
  
  What "Easy to Delete" Actually Means
&lt;/h2&gt;

&lt;p&gt;It doesn't mean write throwaway code. It doesn't mean skip tests or ignore structure.&lt;/p&gt;

&lt;p&gt;It means: &lt;strong&gt;design for reversibility.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you write a feature, ask yourself: &lt;em&gt;if this needed to go away tomorrow, what would that look like?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If the answer is "a 400-line PR touching 20 files," something went wrong at the design stage — not the deletion stage.&lt;/p&gt;

&lt;p&gt;Easy-to-delete code tends to share a few traits:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. It lives in one place
&lt;/h3&gt;

&lt;p&gt;Duplication gets a bad reputation. The DRY principle is good advice, but taken to its extreme, it creates code that's deeply entangled. When the same function is reused in eight different contexts, you can't change it for one context without worrying about all the others.&lt;/p&gt;

&lt;p&gt;Sometimes, a little duplication is the price of independence. Two modules that both have a &lt;code&gt;formatDate&lt;/code&gt; function can each evolve or disappear without consequences.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. It has a clear boundary
&lt;/h3&gt;

&lt;p&gt;The hardest code to delete is the code that has leaked everywhere. The database client that got imported into UI components. The config object that got passed twelve layers deep. The utility function that became load-bearing infrastructure.&lt;/p&gt;

&lt;p&gt;Boundaries are what make deletion safe. An isolated module, a clean interface, a service behind a well-defined API... these are things you can remove, replace, or rewrite without holding or thinking through your breath.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. It doesn't know too much
&lt;/h3&gt;

&lt;p&gt;Code that's easy to delete tends to be ignorant but in the best way. It doesn't know about the rest of the system. It takes inputs, does its job, returns outputs. It doesn't reach out and grab global state. It doesn't mutate things it didn't create.&lt;/p&gt;

&lt;p&gt;Ignorant code is also testable code, which is no coincidence ( I actually didn't wanna add this part for some personal reasons )&lt;/p&gt;

&lt;h3&gt;
  
  
  4. It's hidden behind a seam
&lt;/h3&gt;

&lt;p&gt;Feature flags. Adapter layers. Interface abstractions. These aren't just engineering formalism — they're deletion handles. A feature behind a flag can be switched off in seconds. Code behind an interface can be swapped without the callers noticing.&lt;/p&gt;

&lt;p&gt;The strangler fig pattern exists precisely for this reason: wrap the old thing, build the new thing alongside it, then delete the old thing once it's isolated. The seam is what makes that possible.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Different Way to Think About Abstraction
&lt;/h2&gt;

&lt;p&gt;We often reach for abstraction to avoid repetition. But the best reason to abstract something is to &lt;em&gt;isolate&lt;/em&gt; it or to give it a name and a box so that you can change or remove it without touching everything else.&lt;/p&gt;

&lt;p&gt;Think about logging. You could scatter &lt;code&gt;console.log&lt;/code&gt; calls everywhere. That's easy to write and immediately painful to change. Or you could route all logging through a single &lt;code&gt;logger&lt;/code&gt; module. Now if you want to swap logging libraries, or add context, or silence it entirely — you only touch one file. ONE.&lt;/p&gt;

&lt;p&gt;The abstraction isn't there because logging is complex. It's there because logging is a thing that might &lt;em&gt;change or disappear&lt;/em&gt;, and you want that to be painless.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Abstract at the seams, not in the middle.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Deletability as a Code Review Lens
&lt;/h2&gt;

&lt;p&gt;Here's something I've started doing in code review: asking not just "does this work?" but "what would it take to remove this?"&lt;/p&gt;

&lt;p&gt;It reframes things in a useful way.&lt;/p&gt;

&lt;p&gt;A PR that adds a new feature and touches 15 files is a warning sign — not necessarily because it's wrong, but because it's announcing a high cost of future change. A PR that adds the same feature through a single, well-bounded module is leaving a cleaner footprint.&lt;/p&gt;

&lt;p&gt;You can extend this to architecture decisions. Before adding a new dependency, ask: "what does removing this look like in two years?" Some dependencies are fine because they're small, stable, or isolated. Others are like introducing an invasive species. They grow into everything and become impossible to root out.&lt;/p&gt;




&lt;h2&gt;
  
  
  Impermanence Is Not Defeatism
&lt;/h2&gt;

&lt;p&gt;There's a Zen concept sometimes translated as &lt;em&gt;impermanence&lt;/em&gt; — the idea that things arise, exist for a time, and pass away. This isn't pessimism. It's just an accurate description of how things work.&lt;/p&gt;

&lt;p&gt;Software is the same. Features come and go. Products pivot. Requirements change. The code you're writing today will be partially or wholly replaced. That's not failure — that's how living software works.&lt;/p&gt;

&lt;p&gt;Writing for impermanence means accepting this, and designing accordingly. It means your goal isn't to write code that can never be removed. It's to write code whose removal is &lt;em&gt;cheap&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The engineers who built systems that are still running 30 years later didn't achieve that by making the code impossible to touch. They achieved it by writing code that was easy to reason about, easy to isolate, and when the time came, it's easy to replace piece by piece.&lt;/p&gt;




&lt;h2&gt;
  
  
  In Practice: A Checklist
&lt;/h2&gt;

&lt;p&gt;Before you commit something, it's worth a quick gut-check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Could I delete this feature with a single PR?&lt;/strong&gt; If not, why not?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How many files does this touch?&lt;/strong&gt; More isn't always worse, but it should feel intentional.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is this module aware of things it shouldn't be?&lt;/strong&gt; Imports, globals, side effects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If this dependency disappeared tomorrow, how bad would it be?&lt;/strong&gt; Could you swap it in an afternoon?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Is this abstraction making things easier to change, or just avoiding repetition?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this means paralysis. You don't need to design every microservice like it might vanish. But developing an instinct for deletion cost with the same way you develop an instinct for performance or readability, it'll quietly make your codebases healthier.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Code You Don't Have to Write
&lt;/h2&gt;

&lt;p&gt;There's a final point worth making: the easiest code to delete is the code you never write.&lt;/p&gt;

&lt;p&gt;Every feature is a liability. Every abstraction is a maintenance surface. Every dependency is a relationship you're now in. The code that doesn't exist has no bugs, no coupling, no deletion cost.&lt;/p&gt;

&lt;p&gt;This doesn't mean build nothing. It means be deliberate. When you feel the urge to add a new layer of abstraction, to generalize something that's only been used once, to build for a use case that might never arrive... pause.&lt;/p&gt;

&lt;p&gt;Maybe the right move is to wait. To write the minimal thing. To leave room for deletion.&lt;/p&gt;

&lt;p&gt;Because software that can change easily is software that can survive. And the secret to changeability isn't clever architecture or brilliant abstractions.&lt;/p&gt;

&lt;p&gt;It's knowing that what you built today can be gracefully taken apart tomorrow.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>architecture</category>
    </item>
    <item>
      <title>" SaaS in 2026 Is the Plumbing, Not the Hype "</title>
      <dc:creator>Adam - The Developer</dc:creator>
      <pubDate>Thu, 30 Apr 2026 03:20:11 +0000</pubDate>
      <link>https://dev.to/adamthedeveloper/-kh2</link>
      <guid>https://dev.to/adamthedeveloper/-kh2</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/arunkant/why-im-building-saas-in-2026-55hn" class="crayons-story__hidden-navigation-link"&gt;Why I'm Building SaaS in 2026&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/arunkant/why-im-building-saas-in-2026-55hn" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;SaaS as reliable plumbing for fragile agents&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/arunkant" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F136893%2F76d71e2a-5d18-4fbb-a4fb-33d3a69d532a.png" alt="arunkant profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/arunkant" class="crayons-story__secondary fw-medium m:hidden"&gt;
              arunkant
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                arunkant
                
              
              &lt;div id="story-author-preview-content-3586685" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/arunkant" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F136893%2F76d71e2a-5d18-4fbb-a4fb-33d3a69d532a.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;arunkant&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/arunkant/why-im-building-saas-in-2026-55hn" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Apr 29&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/arunkant/why-im-building-saas-in-2026-55hn" id="article-link-3586685"&gt;
          Why I'm Building SaaS in 2026
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/saas"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;saas&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/agents"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;agents&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/workflows"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;workflows&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/arunkant/why-im-building-saas-in-2026-55hn" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;48&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/arunkant/why-im-building-saas-in-2026-55hn#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              29&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Why High-Performing Teams Look Like They Do Nothing</title>
      <dc:creator>Adam - The Developer</dc:creator>
      <pubDate>Mon, 27 Apr 2026 06:46:12 +0000</pubDate>
      <link>https://dev.to/adamthedeveloper/why-high-performing-teams-look-like-they-do-nothing-2o4j</link>
      <guid>https://dev.to/adamthedeveloper/why-high-performing-teams-look-like-they-do-nothing-2o4j</guid>
      <description>&lt;p&gt;There is a belief quietly embedded in some engineering cultures that suffering equals seriousness. That if your team is not always reachable, not always slightly panicked, not always racing something, then they must not be working hard enough.&lt;/p&gt;

&lt;p&gt;This belief is wrong. And it is worth naming clearly, because it does real damage.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Confusion
&lt;/h2&gt;

&lt;p&gt;Challenge and stress are not the same thing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Challenge&lt;/strong&gt; is being handed a problem you have never solved before and being trusted to figure it out. It is learning something hard. It is designing a system under real constraints. It is disagreeing with a technical decision and having to defend your position with evidence. Challenge stretches you. When it is over, you feel like you grew.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chronic stress&lt;/strong&gt; is your phone buzzing at 10pm. It is a deadline moved up without explanation. It is the ambient anxiety of knowing you must always appear available, always appear busy, always appear to be giving more. When it is over, you feel hollowed out. And then it starts again.&lt;/p&gt;

&lt;p&gt;Some managers treat the second thing as if it were the first. They see responsiveness and call it ownership. They see exhaustion and call it passion. They create urgency as a management style, not because the work demands it, but because urgency feels like leadership.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Happens
&lt;/h2&gt;

&lt;p&gt;It is not always malicious. A lot of it comes from a simple measurement problem.&lt;/p&gt;

&lt;p&gt;Stress is visible. You can see who responds fastest. You can see who is online at midnight. You can see who never pushes back on a deadline. These signals are easy to read, easy to reward, and easy to confuse with performance.&lt;/p&gt;

&lt;p&gt;Genuine challenge is harder to observe. Deep thinking is invisible. The best engineering decisions often look like nothing happened, because a problem was caught early. The developer who said "we should not build this yet" and saved the team three months of rework does not have a story that makes it into the all-hands.&lt;/p&gt;

&lt;p&gt;So the proxy gets promoted: availability, urgency, perpetual motion.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Costs
&lt;/h2&gt;

&lt;p&gt;People in chronically stressed environments often look highly productive for a while. They respond fast. They ship things. They are visibly busy.&lt;/p&gt;

&lt;p&gt;But the quality of their thinking degrades. Their decisions get narrower. They stop exploring better options and start choosing the fastest acceptable one. Their appetite for hard problems shrinks, because they do not have the cognitive space to sit with difficulty. They start optimizing for speed over correctness, for appearing capable over actually being capable.&lt;/p&gt;

&lt;p&gt;And eventually, the best ones leave. Not always loudly. Sometimes they just get quieter, do what is asked, and start looking elsewhere. The people who stay longest in high-stress, low-challenge environments are often those with fewer options, not those with the most to contribute.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Real Challenge Looks Like
&lt;/h2&gt;

&lt;p&gt;This is not an argument against hard work, tight timelines, or high standards. Those things are real, and good engineers often want them.&lt;/p&gt;

&lt;p&gt;Real challenge tends to have a few properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It requires thinking, not just doing.&lt;/strong&gt; The constraint is intellectual, not just temporal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;There is genuine ownership.&lt;/strong&gt; The person doing the work has some say in how it gets done.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The pressure is contextual, not permanent.&lt;/strong&gt; There are hard sprints and then there is recovery. The urgency is tied to real circumstances, not manufactured as a default state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure is information, not catastrophe.&lt;/strong&gt; People can take risks without dreading consequences.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Paranoia is not a feature. Misery is not a signal of commitment. A team that is always stressed is not a team that is being challenged. It is a team that is being depleted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Silence Can Be the Sound of Quality
&lt;/h2&gt;

&lt;p&gt;I experienced this firsthand when I was leading a team.&lt;/p&gt;

&lt;p&gt;We were, by most measures, the quietest team around. Low communication overhead. Calm standups. Not a lot of noise in the incident channels. And the reason was simple: we had very few bugs, and when something did get fixed, it stayed fixed. We were not constantly putting out fires because we were not constantly starting them.&lt;/p&gt;

&lt;p&gt;But I heard through the grapevine that other teams thought we were not doing much. Because we were not loud. Because we were not visibly scrambling.&lt;/p&gt;

&lt;p&gt;Meanwhile, the teams that were most vocal, most communicative, most frantically active were dealing with recurring issues, patches on top of patches, the same problems resurfacing in slightly different shapes. That busyness was real. The work was real. But a significant portion of it was self-generated, the cost of not getting things right the first time.&lt;/p&gt;

&lt;p&gt;The irony is sharp: doing quality work made us invisible. And being invisible got mistaken for being idle.&lt;/p&gt;

&lt;p&gt;This is what happens when communication volume becomes a proxy for productivity. The team fighting fires all day is easy to see. The team that quietly prevented the fires does not have a highlight reel.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Note to Managers
&lt;/h2&gt;

&lt;p&gt;If your team seems disengaged, it is worth asking whether you have been offering them challenge or just stress. The two feel similar from the outside, especially if you are the one setting the pace.&lt;/p&gt;

&lt;p&gt;Challenge requires trust. It means giving someone a genuinely hard problem, stepping back, and letting them work through it. That is harder to do than creating urgency. It requires believing that thinking is work, that slowness can be wisdom, and that the goal is excellent output over a career, not maximum output over a quarter.&lt;/p&gt;

&lt;p&gt;The teams that do the most interesting, lasting work are rarely the ones running on panic. They are the ones who are genuinely absorbed in hard problems, who have space to think, and who know the difference between a real fire and a manufactured one.&lt;/p&gt;

&lt;p&gt;That distinction is worth protecting.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>career</category>
      <category>startup</category>
    </item>
    <item>
      <title>The Developer Who Reviews Everything and Ships Nothing</title>
      <dc:creator>Adam - The Developer</dc:creator>
      <pubDate>Sat, 18 Apr 2026 06:28:10 +0000</pubDate>
      <link>https://dev.to/adamthedeveloper/the-developer-who-reviews-everything-and-ships-nothing-1e28</link>
      <guid>https://dev.to/adamthedeveloper/the-developer-who-reviews-everything-and-ships-nothing-1e28</guid>
      <description>&lt;p&gt;You've seen this person. Maybe you've worked with them for years.&lt;/p&gt;

&lt;p&gt;They leave 40 comments on your PR. Variable names, spacing philosophy, whether your abstraction is "truly necessary," a link to a 2014 blog post about hexagonal architecture. The review sits open for a week. Then two.&lt;/p&gt;

&lt;p&gt;Meanwhile, their own tickets age quietly in the backlog. Untouched.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before We Go There
&lt;/h2&gt;

&lt;p&gt;Most strict reviewers are not villains.&lt;/p&gt;

&lt;p&gt;A lot of them have been burned. They've watched a rushed merge take down production at 2am. They've inherited a codebase where "we'll clean it up later" compounded for three years into something unmaintainable. Strictness in review often comes from real scar tissue. That's not dysfunction. That's experience talking.&lt;/p&gt;

&lt;p&gt;This article isn't about those people.&lt;/p&gt;

&lt;p&gt;It's about a specific, observable pattern: the developer whose review activity is consistently high, whose shipping activity is consistently low, and whose involvement reliably increases cycle time without a corresponding increase in outcome quality. That pattern has a real cost and it rarely gets named out loud.&lt;/p&gt;

&lt;p&gt;So let's name it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Pattern
&lt;/h2&gt;

&lt;p&gt;It doesn't require mind-reading. It shows up in the data.&lt;/p&gt;

&lt;p&gt;PRs they author are rare. When they do appear, they're small, low-risk, or six weeks overdue. PRs they touch accumulate long threads, mostly non-blocking comments that aren't labeled as such, leaving authors to guess what actually needs to change. Review cycles on their queue run longer than the team average. Features that slip usually have their fingerprints somewhere in the timeline.&lt;/p&gt;

&lt;p&gt;That's the shape of it. Measurable. Repeatable. Worth paying attention to.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where They Put Their Time
&lt;/h2&gt;

&lt;p&gt;Senior engineers have limited hours. Where those hours go is a signal.&lt;/p&gt;

&lt;p&gt;A senior spending eight hours a week in code review and two hours writing code has made a choice. Sometimes that's the right call — architecture, incident response, debugging gnarly production issues, mentorship. Real contributions that don't show up in merge counts.&lt;/p&gt;

&lt;p&gt;But when the same person's review comments outnumber their merged commits by a factor of ten, quarter after quarter, you're looking at someone who has concentrated their influence in the one place where they can evaluate others without being evaluated themselves.&lt;/p&gt;

&lt;p&gt;That asymmetry is the tell.&lt;/p&gt;




&lt;h2&gt;
  
  
  What It Actually Costs
&lt;/h2&gt;

&lt;p&gt;When review cycles drag on for days, the effects compound quietly.&lt;/p&gt;

&lt;p&gt;The author loses context. They've moved on mentally to the next problem. When they return to address 30 comments, they're doing archaeology on their own work.&lt;/p&gt;

&lt;p&gt;Junior developers learn that shipping is scary. That there's always something wrong. That the bar is impossibly high, so maybe it's better to ask fewer questions and wait for someone to tell you what to do.&lt;/p&gt;

&lt;p&gt;Momentum dies. Not in one dramatic moment, but comment by comment, week by week.&lt;/p&gt;

&lt;p&gt;And the developer with the high standards? Present at every standup. Very visible. Very engaged. Zero shipped features to show for the sprint.&lt;/p&gt;




&lt;h2&gt;
  
  
  Nitpicking Is Not Mentorship
&lt;/h2&gt;

&lt;p&gt;There's a version of this that gets laundered through mentorship language.&lt;/p&gt;

&lt;p&gt;"I'm just trying to teach them." "How else will they learn?" "I wouldn't be doing my job if I let this slide."&lt;/p&gt;

&lt;p&gt;Genuine mentorship explains tradeoffs. It asks questions instead of demanding changes. It approves code that's good enough while offering perspective on what could be better next time.&lt;/p&gt;

&lt;p&gt;What it doesn't do is make someone feel like their work is never good enough, while that same reviewer's own code somehow never faces the same gauntlet.&lt;/p&gt;

&lt;p&gt;If your "mentorship" only flows one direction and none of your mentees can ship without your sign-off on 40 line items, that's not mentorship. That's a bottleneck with good branding.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Accountability Gap
&lt;/h2&gt;

&lt;p&gt;Here's a diagnostic worth running.&lt;/p&gt;

&lt;p&gt;Track for one month which developers on your team ship production code. Not who reviews, not who comments — who actually merges working features to production. Then look at who has the most comments open across other people's PRs.&lt;/p&gt;

&lt;p&gt;A large gap between those two lists is worth investigating. Not a verdict. Seniors do invisible work that won't show up in a merge count, and that work genuinely matters.&lt;/p&gt;

&lt;p&gt;But here's the cut: if someone's review involvement is consistently high, their direct output is consistently low, and the team's cycle time is getting worse, "I do invisible work" becomes a harder argument to sustain. Shipping isn't the only form of contribution. But consistent absence from shipping alongside heavy review influence is a smell, and pretending otherwise doesn't make it go away.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fixes With Actual Teeth
&lt;/h2&gt;

&lt;p&gt;The standard advice is: add review SLAs, separate blocking from non-blocking comments, track cycle time. All correct. All worth doing. All also pretty easy to quietly ignore.&lt;/p&gt;

&lt;p&gt;If the pattern is entrenched, you need something sharper.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cap non-blocking comments.&lt;/strong&gt; If a reviewer leaves more than five non-blocking comments, they roll them into a summary or they stay quiet. Unlimited low-stakes commentary costs the reviewer nothing and costs the author a morning. Change the economics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Require a patch for strong objections.&lt;/strong&gt; If a reviewer argues an approach is wrong, they should be able to show an alternative. Not to embarrass anyone — because it forces the objection to get concrete. A lot of "this architecture is problematic" comments evaporate the moment someone has to write the better version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make the ratio visible.&lt;/strong&gt; Track review comments opened vs. PRs merged per person, alongside cycle time per reviewer. Don't make it a performance metric. Just make it visible. Sunlight is usually enough.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Know who actually has veto power.&lt;/strong&gt; Not every PR needs every senior. Be explicit about who is a required approver versus who is optional feedback. When everyone with an opinion is a required approver, you've handed veto power to whoever is most willing to hold out longest.&lt;/p&gt;




&lt;h2&gt;
  
  
  Raising the Bar vs. Holding the Door Shut
&lt;/h2&gt;

&lt;p&gt;You can tell the difference by asking one question: does this person's involvement make the team ship more, or less?&lt;/p&gt;

&lt;p&gt;If every PR they touch becomes a negotiation, if every week they're reviewing has longer cycle times than the weeks they're out, if junior developers dread their feedback instead of seeking it — that's your answer.&lt;/p&gt;

&lt;p&gt;Raising the bar means the team gets better over time. Patterns become consistent. People grow and ship with more confidence.&lt;/p&gt;

&lt;p&gt;Holding the door shut means nothing gets through without a fight, nobody proposes anything ambitious, and your most capable people quietly start updating their resumes.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Mirror
&lt;/h2&gt;

&lt;p&gt;When did you last ship something? When did you last put your own code up for the same level of scrutiny you apply to others?&lt;/p&gt;

&lt;p&gt;If your presence in the review process reliably slows shipping more than it improves outcomes, you are not raising standards. You are taxing the team.&lt;/p&gt;

&lt;p&gt;The engineers who actually elevate a codebase over time make things better and faster simultaneously. That's the bar. It's harder than leaving comments.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>programming</category>
      <category>career</category>
      <category>learning</category>
    </item>
    <item>
      <title>Your Caching Strategy Is Not a Strategy</title>
      <dc:creator>Adam - The Developer</dc:creator>
      <pubDate>Mon, 06 Apr 2026 04:46:45 +0000</pubDate>
      <link>https://dev.to/adamthedeveloper/your-caching-strategy-is-not-a-strategy-1he5</link>
      <guid>https://dev.to/adamthedeveloper/your-caching-strategy-is-not-a-strategy-1he5</guid>
      <description>&lt;p&gt;You have a slow endpoint. Someone suggests Redis. You add Redis. The endpoint gets faster. You ship it. Six months later you are debugging a production incident where users see stale data, your cache hit rate is 12%, and you have no idea what is actually in Redis anymore.&lt;/p&gt;

&lt;p&gt;That is not a caching strategy. That is a prayer with an expiry time.&lt;/p&gt;

&lt;p&gt;This post is not here to roast you. It is here to give you the patterns, the code, and the mental model to do this right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Caching Is a Contract&lt;/li&gt;
&lt;li&gt;The Three Classic Patterns (And When to Use Each)&lt;/li&gt;
&lt;li&gt;Build a Cache Client Worth Using&lt;/li&gt;
&lt;li&gt;Cache Key Design: More Important Than It Looks&lt;/li&gt;
&lt;li&gt;
Cache Invalidation: The Part Everyone Skips

&lt;ul&gt;
&lt;li&gt;TTL vs. Event Driven Invalidation&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

The Thundering Herd and How to Solve It

&lt;ul&gt;
&lt;li&gt;Solution 1: Jitter&lt;/li&gt;
&lt;li&gt;Solution 2: Stale While Revalidate&lt;/li&gt;
&lt;li&gt;Solution 3: Distributed Lock on Cache Miss&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Observability: Know What Is Actually Happening&lt;/li&gt;

&lt;li&gt;The Decision Checklist Before You Add a Cache&lt;/li&gt;

&lt;li&gt;Putting It All Together&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Caching Is a Contract
&lt;/h2&gt;

&lt;p&gt;Before you touch Redis, understand what you are agreeing to.&lt;/p&gt;

&lt;p&gt;Caching is a contract. You are telling your system: "I accept that this data may be slightly wrong for a period of time, in exchange for speed." Most teams sign that contract without reading it.&lt;/p&gt;

&lt;p&gt;Before you add any cache entry, answer these four questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What is the acceptable staleness window for this data?&lt;/li&gt;
&lt;li&gt;Who invalidates this entry and when?&lt;/li&gt;
&lt;li&gt;What happens when the cache is cold?&lt;/li&gt;
&lt;li&gt;What happens when the cache is wrong?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you cannot answer all four, you do not have a caching strategy. You have optimism.&lt;/p&gt;

&lt;p&gt;The rest of this post is about answering each of those questions with real code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Classic Patterns (And When to Use Each)
&lt;/h2&gt;

&lt;p&gt;There are three fundamental ways to integrate a cache with your database. Most teams only know one and use it everywhere.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;How It Works&lt;/th&gt;
&lt;th&gt;When to Use It&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cache Aside&lt;/td&gt;
&lt;td&gt;App checks cache, misses go to DB, app writes to cache&lt;/td&gt;
&lt;td&gt;Default. Works well for most read heavy workloads.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write Through&lt;/td&gt;
&lt;td&gt;Every write goes to DB and cache together, atomically&lt;/td&gt;
&lt;td&gt;Read heavy data that changes infrequently. Keeps cache always warm.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write Behind&lt;/td&gt;
&lt;td&gt;Write to cache immediately, flush to DB asynchronously&lt;/td&gt;
&lt;td&gt;Very high write throughput: analytics, metrics, event ingestion, rate limiting counters.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Most engineers default to cache aside everywhere, which is fine until it is not. Write behind in particular is underused. When you are recording analytics events or incrementing rate limit counters, you do not need each write to round trip to a database. Write to Redis, flush to Postgres in batches. Your database handles a fraction of the load.&lt;/p&gt;

&lt;p&gt;The important thing is that you choose consciously. Each pattern has tradeoffs. Write behind carries real risk of data loss if Redis fails before the flush. That is acceptable for a view counter and unacceptable for a financial transaction. Know which one you are dealing with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a Cache Client Worth Using
&lt;/h2&gt;

&lt;p&gt;Before diving into patterns, establish a typed, reusable cache client. This becomes the foundation for everything below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RedisClientType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;redis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CacheOptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// base TTL in seconds&lt;/span&gt;
  &lt;span class="nl"&gt;jitter&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// max random seconds to add (prevents stampedes)&lt;/span&gt;
  &lt;span class="nl"&gt;stale&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// extra seconds to serve stale while revalidating&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CacheEntry&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cachedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;expiresAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CacheClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RedisClientType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redisUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;redisUrl&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;RedisClientType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nf"&gt;getUnderlyingClient&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;RedisClientType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;effectiveTTL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CacheOptions&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jitter&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jitter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ttl&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;jitter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CacheEntry&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;CacheEntry&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CacheOptions&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ttl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;effectiveTTL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;staleTTL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ttl&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stale&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CacheEntry&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;cachedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;expiresAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ttl&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setEx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;staleTTL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;del&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;del&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;delByPattern&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;del&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;CacheEntry&lt;/code&gt; wrapper stores &lt;code&gt;cachedAt&lt;/code&gt; and &lt;code&gt;expiresAt&lt;/code&gt; alongside the value. This unlocks stale while revalidate later without a second Redis call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cache Key Design: More Important Than It Looks
&lt;/h2&gt;

&lt;p&gt;This is one of the most overlooked parts of a caching system. Your key schema is an architectural decision, not a naming convention.&lt;/p&gt;

&lt;p&gt;A good cache key answers five questions, in order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app : v2 : user : 123 : profile
 ^     ^     ^     ^      ^
 |     |     |     |      shape or query variant
 |     |     |     entity ID
 |     |     entity type
 |     cache version
 app namespace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;:&lt;span class="n"&gt;v2&lt;/span&gt;:&lt;span class="n"&gt;user&lt;/span&gt;:&lt;span class="m"&gt;123&lt;/span&gt;:&lt;span class="n"&gt;permissions&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;:&lt;span class="n"&gt;v2&lt;/span&gt;:&lt;span class="n"&gt;feed&lt;/span&gt;:&lt;span class="n"&gt;user&lt;/span&gt;:&lt;span class="m"&gt;123&lt;/span&gt;:&lt;span class="n"&gt;page&lt;/span&gt;:&lt;span class="m"&gt;2&lt;/span&gt;:&lt;span class="n"&gt;limit&lt;/span&gt;:&lt;span class="m"&gt;20&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;:&lt;span class="n"&gt;v2&lt;/span&gt;:&lt;span class="n"&gt;product&lt;/span&gt;:&lt;span class="m"&gt;456&lt;/span&gt;:&lt;span class="n"&gt;inventory&lt;/span&gt;:&lt;span class="n"&gt;warehouse&lt;/span&gt;:&lt;span class="n"&gt;uk&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bad keys look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user:123
profile_123_v2
user_data_new_123
temp_user_123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No namespace means you cannot isolate patterns for deletion. No version means changing the shape of a cached object requires flushing all of Redis. No structure means you cannot delete "everything for user 123" with a single pattern.&lt;/p&gt;

&lt;p&gt;The key schema also tells you something about your architecture. If your keys look inconsistent, your caching layer grew organically without a plan. Standardize the schema early and enforce it through a key builder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CACHE_VERSION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CACHE_VERSION&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;v1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;userProfile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="s2"&gt;`app:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;CACHE_VERSION&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:user:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:profile`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;userPermissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="s2"&gt;`app:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;CACHE_VERSION&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:user:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:permissions`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;userFeed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="s2"&gt;`app:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;CACHE_VERSION&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:feed:user:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:page:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:limit:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;userAll&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="s2"&gt;`app:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;CACHE_VERSION&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:user:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:*`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when you need to invalidate all data for a user, it is one call: &lt;code&gt;delByPattern(keys.userAll(userId))&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To handle a breaking shape change, bump &lt;code&gt;CACHE_VERSION&lt;/code&gt; in your deploy config. Old keys expire naturally. New requests populate the new shape. No coordinated flush against production Redis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cache Invalidation: The Part Everyone Skips
&lt;/h2&gt;

&lt;p&gt;Most cache bugs are not "we cached the wrong thing." They are "we forgot to uncache it when the underlying data changed."&lt;/p&gt;

&lt;p&gt;Here is the pattern that causes incidents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The read path is carefully thought through&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUserProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;userProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cached&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setEx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// The write path does not think about the cache at all&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateUserProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// cache is now wrong. silently. for the next hour.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix is to own both paths in the same service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CacheClient&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserProfile&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;userProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserProfile&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;jitter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;updateProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserProfile&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserProfile&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Write through: update cache immediately with fresh data&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;userProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;updated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;jitter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;updated&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;suspendAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;suspendUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Permissions must be immediately consistent.&lt;/span&gt;
    &lt;span class="c1"&gt;// Never serve stale permission data. Delete, do not wait for TTL.&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;del&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;userPermissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice &lt;code&gt;suspendAccount&lt;/code&gt; does not use write through. It deletes the permissions key outright. Serving a stale permission is a security issue, not a UX issue. The next read will hit the database and get the correct answer.&lt;/p&gt;

&lt;h3&gt;
  
  
  TTL vs. Event Driven Invalidation
&lt;/h3&gt;

&lt;p&gt;These are two different tools for two different problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TTL invalidation&lt;/strong&gt; is for data where being slightly stale is acceptable. Exchange rates, public blog posts, product catalog pages. Set a TTL and let entries expire naturally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Event driven invalidation&lt;/strong&gt; is for data where correctness matters. User permissions, account status, pricing. Delete or update the cache entry at the moment of the change, not on a timer.&lt;/p&gt;

&lt;p&gt;Most systems use TTL for everything because it requires less upfront thinking. Then they get burned when a permissions update does not take effect for an hour. The fix is not to lower the TTL. Lowering the TTL is how you get a thundering herd.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Thundering Herd and How to Solve It
&lt;/h2&gt;

&lt;p&gt;A typical Redis hit takes 1 to 3ms. A typical database query takes 50 to 300ms. That means one cache miss can cost as much as 100 cache hits. At scale, getting this wrong does not just make things slow. It brings services down.&lt;/p&gt;

&lt;p&gt;Imagine a hot cache entry expires at 3:00:00 AM. You have 500 concurrent users. At 3:00:01, all 500 get a cache miss simultaneously and fire a database query. Your database, handling 10 queries per second comfortably, suddenly receives 500 in the same second and collapses.&lt;/p&gt;

&lt;p&gt;You just traded slightly stale data for a full outage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Without protection:

500 requests ──► 500 DB queries ──► DB overwhelmed ──► Outage

With jitter + lock:

500 requests ──► 1 DB query ──► Cache filled ──► 499 served from cache
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three solutions, applied in layers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution 1: Jitter
&lt;/h3&gt;

&lt;p&gt;The simplest fix. Add randomness to expiry so entries do not all expire at the same moment. Already built into the &lt;code&gt;CacheClient&lt;/code&gt; above via the &lt;code&gt;jitter&lt;/code&gt; option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Without jitter: 500 entries for a popular endpoint all expire at 03:00:00&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// With jitter: entries expire anywhere between 3600 and 3900 seconds&lt;/span&gt;
&lt;span class="c1"&gt;// Stampede risk drops dramatically for zero added complexity&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;jitter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add &lt;code&gt;jitter&lt;/code&gt; everywhere. It costs nothing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution 2: Stale While Revalidate
&lt;/h3&gt;

&lt;p&gt;Serve the stale entry immediately. Trigger a background refresh. The next request gets fresh data. This is how HTTP caching has worked for decades.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Request ──► Cache hit? ──► Yes, and fresh? ──► Return immediately
                │
                │ Yes, but stale?
                ├──► Return immediately (user does not wait)
                │    └──► Trigger background refresh
                │
                │ No (full miss)
                └──► Fetch from DB ──► Populate cache ──► Return
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;FetchFn&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getWithStaleRevalidate&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CacheClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;fetchFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FetchFn&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CacheOptions&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;stale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isStale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expiresAt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isStale&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Serve the stale value immediately, refresh in the background&lt;/span&gt;
      &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;refreshInBackground&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fetchFn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Full miss: fetch synchronously&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchFn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;refreshInBackground&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CacheClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;fetchFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FetchFn&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CacheOptions&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchFn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Background cache refresh failed for &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage at call sites is one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getWithStaleRevalidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;userProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;stale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;jitter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Users never wait on a cache refresh. The background task handles it. The next user gets the fresh value.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution 3: Distributed Lock on Cache Miss
&lt;/h3&gt;

&lt;p&gt;For hot single keys, when a miss happens only one process should fetch and repopulate. Others wait briefly. This prevents 500 processes all querying the database for the same row at the same time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Redlock&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;redlock&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LockedCacheClient&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;CacheClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;redlock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Redlock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redisUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redisUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;redlock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Redlock&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUnderlyingClient&lt;/span&gt;&lt;span class="p"&gt;()]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;getOrFetch&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;fetchFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FetchFn&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CacheOptions&lt;/span&gt;
  &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lockKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`lock:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;redlock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;acquire&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;lockKey&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// Re-check after acquiring the lock.&lt;/span&gt;
      &lt;span class="c1"&gt;// Another process may have already populated the cache while we waited.&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recheck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recheck&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;recheck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchFn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Lock contention: fall back to a direct DB fetch rather than failing the request&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchFn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The re-check after acquiring the lock is critical. Without it, the second process acquires the lock and queries the database anyway even though the first process just populated the cache. This is the double checked locking pattern applied to distributed systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Observability: Know What Is Actually Happening
&lt;/h2&gt;

&lt;p&gt;You cannot improve what you cannot see. Wrap your cache client with metrics once and label call sites with a pattern name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Histogram&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Registry&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prom-client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ObservableCacheClient&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;CacheClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;misses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;hitLatency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Histogram&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;missLatency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Histogram&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redisUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Registry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redisUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cache_hits_total&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;help&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Total cache hits&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;labelNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;key_pattern&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;registers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;misses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cache_misses_total&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;help&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Total cache misses&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;labelNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;key_pattern&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;registers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hitLatency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Histogram&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cache_hit_duration_seconds&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;help&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Latency of cache hits&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;labelNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;key_pattern&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;registers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;missLatency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Histogram&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cache_miss_duration_seconds&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;help&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Latency of cache misses including the upstream fetch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;labelNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;key_pattern&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;registers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;getOrFetchObserved&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;keyPattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;fetchFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FetchFn&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CacheOptions&lt;/span&gt;
  &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elapsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inc&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;key_pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;keyPattern&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hitLatency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;key_pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;keyPattern&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nf"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;misses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inc&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;key_pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;keyPattern&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchFn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;missLatency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;key_pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;keyPattern&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nf"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The three metrics that matter most:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hit rate per key pattern, not aggregate.&lt;/strong&gt; If your overall hit rate is 80% but a critical key pattern sits at 20%, the aggregate number is hiding a real problem. Always break this down by pattern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Eviction rate.&lt;/strong&gt; If Redis is evicting keys because you are out of memory, you are thrashing, not caching. A Redis hit at 1ms becomes meaningless if the key you need was evicted 30 seconds ago. Increase memory, shorten TTLs, or stop caching data that expires before it is ever read again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Miss latency.&lt;/strong&gt; At scale, a cache miss costs 50 to 300ms of database time. If your service depends on sub-10ms responses, one cold endpoint can blow your entire p99. Miss latency tells you exactly how bad the degradation is when your cache fails.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Decision Checklist Before You Add a Cache
&lt;/h2&gt;

&lt;p&gt;Not every performance problem needs a cache. Run through this before reaching for Redis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is the query actually slow or just called too often?&lt;/strong&gt; N+1 patterns make caching look like the answer when a JOIN is. Profile the query execution plan before adding a cache layer on top of a structural problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can the data be denormalized instead?&lt;/strong&gt; If you always cache the same join result, consider materializing it in the schema. A cache is sometimes a workaround for a schema designed for write convenience rather than read performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is this actually read heavy?&lt;/strong&gt; If data is written more often than it is read, cache invalidation overhead exceeds the savings. You are paying the write cost twice: once to the database, once to update or invalidate the cache entry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the cost of serving stale data?&lt;/strong&gt; Product listings: probably acceptable. Account balance: no. Permissions and access control: never. Make this decision explicitly. Saying "we will just set a short TTL" is not an answer. It is a way of avoiding the question.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is your cold start story?&lt;/strong&gt; If your service falls over every time it restarts because the cache is cold, write through caching or a warming script is the fix, not hoping traffic is light during the deploy window.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;Here is a service that uses everything from this post: typed versioned keys, write through on mutations, stale while revalidate on reads, jitter throughout, immediate delete for permissions, and full observability.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductionUserService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ObservableCacheClient&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserProfile&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;getWithStaleRevalidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;userProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;stale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;jitter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;updateProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserProfile&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserProfile&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Write through: fresh data goes straight to cache on every mutation&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;userProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nx"&gt;updated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;stale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;jitter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;updated&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;updatePermissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Permission&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updatePermissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Access control data is deleted immediately, never served stale&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;del&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;userPermissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reads use stale while revalidate so users never block on a cache refresh. Profile writes use write through so the cache stays warm after every mutation. Permission changes delete immediately because serving a stale permission is a security issue, not a UX issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;The teams that handle caching well treat it as a first class architectural concern. They document cache contracts: what is cached, for how long, what triggers invalidation, and what the acceptable staleness window is. They test cold start and stampede scenarios explicitly. They monitor cache health with the same seriousness as database health.&lt;/p&gt;

&lt;p&gt;The teams that handle caching poorly add Redis when things get slow and ship it.&lt;/p&gt;

&lt;p&gt;Both approaches work right up until they do not. The difference is that the first team knows exactly what breaks and why when it does. The second team opens their laptop at past midnight and starts reading documentation for the first time or throw in " pls help me, redis no work, idk what is redis no more " into ChatGPT.&lt;/p&gt;

&lt;p&gt;The patterns in this post are not theoretical. They are the things you wish were already in the codebase when the incident starts. Put them in before it does.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>performance</category>
      <category>redis</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Episode 2: I Was a Junior Developer and I Must Be Stopped</title>
      <dc:creator>Adam - The Developer</dc:creator>
      <pubDate>Thu, 02 Apr 2026 03:36:54 +0000</pubDate>
      <link>https://dev.to/adamthedeveloper/episode-2-i-was-a-junior-developer-and-i-must-be-stopped-1jom</link>
      <guid>https://dev.to/adamthedeveloper/episode-2-i-was-a-junior-developer-and-i-must-be-stopped-1jom</guid>
      <description>&lt;p&gt;Welcome back.&lt;/p&gt;

&lt;p&gt;Last episode, we reviewed a Laravel function that used three loops to reconstruct an array it already had, fired two database queries per item instead of one, and named a variable &lt;code&gt;$r_arr&lt;/code&gt; with the confidence of a man who has never once been held accountable for anything.&lt;/p&gt;

&lt;p&gt;It was a good one, some of us liked it.&lt;/p&gt;

&lt;p&gt;So today, we are reviewing my hotel invoice generator. It is a PHP class called &lt;code&gt;ExcelController&lt;/code&gt;. It imports guest data from an uploaded Excel file and exports a personalised invoice for each guest, packed into a zip file for download.&lt;/p&gt;

&lt;p&gt;It was never merged.&lt;/p&gt;

&lt;p&gt;The room numbers are completely random.&lt;/p&gt;

&lt;p&gt;My senior opened the PR. He read through it. He left seven comments. He never raised his voice. He never sent a follow-up Slack. He just reviewed it with the quiet, deliberate calm of a man who has already accepted his fate and is simply choosing, each day, to keep showing up anyway.&lt;/p&gt;

&lt;p&gt;Here it is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExcelController&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;ImportedFile&lt;/span&gt; &lt;span class="nv"&gt;$filedata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Excelconfig&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'file'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;back&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'error'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Please choose a file to import'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="nv"&gt;$spreadsheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;IOFactory&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nv"&gt;$numSheets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$spreadsheet&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getSheetCount&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$numSheets&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;back&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'error'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Excel file has more than one sheet.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="nc"&gt;Excel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DataImport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$filedata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;back&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'File imported successfully.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\Exception&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;rollBack&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;back&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'error'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'File import failed: '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$filedata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$getfile_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'default_file'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="nv"&gt;$file_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;base_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/storage/app/main_file/&lt;/span&gt;&lt;span class="nv"&gt;$getfile_name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$file_path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$file_path&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nv"&gt;$file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;base_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/storage/app/main_file/main_file.xls"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$num_copies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$filedata&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$spreadsheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;IOFactory&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$guest_names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$filedata&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'guest_name'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$invoice_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$filedata&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'reservation_number'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$payment_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$filedata&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'payment_date'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$room&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Room"&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;" "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$nights_stayed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$filedata&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'number_nights_stayed'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$filedata&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'total_revenue'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$zip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ZipArchive&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$zip_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Ymd_His'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'.zip'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$zip&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;storage_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app/'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$zip_name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;ZipArchive&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CREATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nv"&gt;$num_copies&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nv"&gt;$zip_file_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Guest'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'.xlsx'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nv"&gt;$worksheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$spreadsheet&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getActiveSheet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

                &lt;span class="nv"&gt;$message_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="nv"&gt;$_guestNameRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'guest_name'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'A21'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nv"&gt;$_invoiceNumRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'invoice_number'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'B15'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nv"&gt;$_dateRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'payment_date'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'B17'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nv"&gt;$_roomRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'room'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'B21'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nv"&gt;$_quantity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'nights_stayed'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'C21'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nv"&gt;$_unitPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'unit_price'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'D21'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nv"&gt;$_amountPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'amount_price'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'E21'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nv"&gt;$_amountTotal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'amount_price_row_total'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'D34'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nv"&gt;$_unitTotal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'unit_price_total'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'E34'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="nv"&gt;$spreadsheet&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getActiveSheet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCellValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_guestNameRow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$guest_names&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$message_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nv"&gt;$spreadsheet&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getActiveSheet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCellValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_invoiceNumRow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$invoice_number&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$message_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nv"&gt;$spreadsheet&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getActiveSheet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCellValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_dateRow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$payment_date&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$message_index&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
                &lt;span class="nv"&gt;$spreadsheet&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getActiveSheet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCellValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_roomRow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$room&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nv"&gt;$spreadsheet&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getActiveSheet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCellValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$nights_stayed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$message_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nv"&gt;$spreadsheet&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getActiveSheet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCellValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_unitPrice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$price&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$message_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$nights_stayed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$message_index&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nv"&gt;$spreadsheet&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getActiveSheet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCellValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_amountPrice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$price&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$message_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nv"&gt;$spreadsheet&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getActiveSheet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCellValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_unitTotal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$price&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$message_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$nights_stayed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$message_index&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nv"&gt;$spreadsheet&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getActiveSheet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCellValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_amountTotal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$price&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$message_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="nv"&gt;$writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Xlsx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$spreadsheet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nb"&gt;ob_start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="nv"&gt;$writer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'php://output'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nv"&gt;$file_contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;ob_get_clean&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="nv"&gt;$zip&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addFromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$zip_file_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$file_contents&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nv"&gt;$zip&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Content-Type: application/zip"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Content-Disposition: attachment; filename=&lt;/span&gt;&lt;span class="nv"&gt;$zip_name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Content-Length: "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;filesize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;storage_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app/'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$zip_name&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
            &lt;span class="nb"&gt;readfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;storage_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app/'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$zip_name&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take it in. Sit with it. Let it wash over you like a wave you saw coming from a mile away and decided not to move for.&lt;/p&gt;

&lt;p&gt;Now. Let's go through it together. I recommend water. I recommend a snack. I recommend telling someone you love them before we start, just in case.&lt;/p&gt;




&lt;h2&gt;
  
  
  The &lt;code&gt;import()&lt;/code&gt; Method, Which Does Not Import
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;ImportedFile&lt;/span&gt; &lt;span class="nv"&gt;$filedata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Excelconfig&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four injected dependencies. FOUR. &lt;code&gt;Request&lt;/code&gt;. &lt;code&gt;ImportedFile&lt;/code&gt;. &lt;code&gt;User&lt;/code&gt;. &lt;code&gt;Excelconfig&lt;/code&gt;. A full starting lineup. An ensemble cast. A heist crew assembled with purpose and intention.&lt;/p&gt;

&lt;p&gt;And what does &lt;code&gt;import()&lt;/code&gt; do with them? It validates the file exists, hands it to &lt;code&gt;Excel::import()&lt;/code&gt;, and then — immediately, in the next breath, before the import has even had time to feel good about itself — calls &lt;code&gt;$this-&amp;gt;export()&lt;/code&gt; and passes all four dependencies over like a baton.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import()&lt;/code&gt; does not import. &lt;code&gt;import()&lt;/code&gt; is a method that watches another method work and then takes credit at standup. &lt;code&gt;import()&lt;/code&gt; is the guy who responds "yeah, we shipped that" while making eye contact with the person who actually shipped it, daring them to say something.&lt;/p&gt;

&lt;p&gt;Three of the four injected dependencies are not used in &lt;code&gt;import()&lt;/code&gt; at all. They exist solely to be forwarded. They showed up for a job they were not given, were immediately redirected, and had to pretend that was the plan all along.&lt;/p&gt;

&lt;p&gt;My senior's very first comment on this PR was: "why does import() call export()?"&lt;/p&gt;

&lt;p&gt;Not aggressive. Not horrified. Just a quiet, five-word question with a question mark that somehow conveyed the weight of every PR he had ever reviewed before this one.&lt;/p&gt;

&lt;p&gt;I did not have a good answer. I replied anyway. The reply was not good.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Rollback That Has Nothing to Roll Back
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\Exception&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;rollBack&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;back&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'error'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'File import failed: '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no &lt;code&gt;DB::beginTransaction()&lt;/code&gt; anywhere in this class.&lt;/p&gt;

&lt;p&gt;I want you to understand the full scope of what has happened here. &lt;code&gt;DB::rollBack()&lt;/code&gt; is an instruction to undo a database transaction. A transaction that was never opened. There is nothing to undo. There has never been anything to undo. The database received this rollback instruction, checked its records, found no open transaction, shrugged in whatever way databases are capable of shrugging, and moved on.&lt;/p&gt;

&lt;p&gt;This is not a bug. It's a philosophy. It's the worldview of someone who believes that calling &lt;code&gt;rollBack()&lt;/code&gt; &lt;em&gt;near&lt;/em&gt; a database operation is close enough. The vibes are correct. The implementation is a void.&lt;/p&gt;

&lt;p&gt;It's like calling &lt;code&gt;refund()&lt;/code&gt; on a purchase you never made. It's like issuing a formal apology for something you didn't do. It's like cancelling a reservation at a restaurant you never booked, and then waiting at home for confirmation that you don't have a table.&lt;/p&gt;

&lt;p&gt;My senior's comment here was two words: "no transaction."&lt;/p&gt;

&lt;p&gt;Two words. No question mark. No exclamation. Two words, written by a man conserving his energy for what he was about to find next.&lt;/p&gt;




&lt;h2&gt;
  
  
  User ID 1. Just User ID 1. Only Ever User ID 1.
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$getfile_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'default_file'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This system has one user. His name is User ID 1. He is the client, the admin, the guest, and apparently the sole known resident of the hotel this invoice generator was built for. He has no colleagues. He has no hierarchy above or below him. He simply &lt;em&gt;is&lt;/em&gt;, hardcoded and eternal, &lt;code&gt;where('id', 1)&lt;/code&gt;, forever.&lt;/p&gt;

&lt;p&gt;Multi-tenancy is a concept this code has never heard of. Role-based access control is a myth, like dragons, or good variable names. There is only User ID 1. He is in the controller. He is in the database. He is in my code. He is everywhere. He always will be.&lt;/p&gt;

&lt;p&gt;To be clear: this was a single-client internal tool. There technically was only one user. But still. To look at a codebase, decide that &lt;code&gt;-&amp;gt;where('id', 1)&lt;/code&gt; is fine to hardcode into a controller, and commit it — that's not just a shortcut. That's a statement of values.&lt;/p&gt;




&lt;h2&gt;
  
  
  The &lt;code&gt;if&lt;/code&gt; Block That Tried So Hard and Was Immediately Betrayed
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$file_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;base_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/storage/app/main_file/&lt;/span&gt;&lt;span class="nv"&gt;$getfile_name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$file_path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$file_path&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;$file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;base_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/storage/app/main_file/main_file.xls"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We check whether a dynamic file exists. If it does, we assign it to &lt;code&gt;$file&lt;/code&gt;. This is good. This is correct. This is the code doing exactly what code is supposed to do.&lt;/p&gt;

&lt;p&gt;And then — on the very next line, with no condition, no ceremony, no acknowledgment whatsoever of what just happened — we overwrite &lt;code&gt;$file&lt;/code&gt; with a hardcoded path to &lt;code&gt;main_file.xls&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;if&lt;/code&gt; block is a ghost. It built &lt;code&gt;$file&lt;/code&gt; with love and intention, watched it get immediately overwritten, and now haunts the codebase unable to affect anything, just flickering the lights occasionally to let us know it was here once and it mattered and we chose not to listen.&lt;/p&gt;

&lt;p&gt;Every single guest received the same template. The dynamic file path never made it. The &lt;code&gt;if&lt;/code&gt; block knew. It stood there and watched and couldn't stop it.&lt;/p&gt;

&lt;p&gt;I think about this &lt;code&gt;if&lt;/code&gt; block sometimes. I think about its optimism. I think about how it ran, found the file, did its job perfectly, and then got erased before it could even tell anyone.&lt;/p&gt;

&lt;p&gt;We're not so different, the &lt;code&gt;if&lt;/code&gt; block and I.&lt;/p&gt;




&lt;h2&gt;
  
  
  Six Queries. Same Table. In a Row. Like a Person Who Has Completely Lost the Plot.
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$num_copies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$filedata&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// query 1&lt;/span&gt;
&lt;span class="nv"&gt;$guest_names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$filedata&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'guest_name'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;       &lt;span class="c1"&gt;// query 2&lt;/span&gt;
&lt;span class="nv"&gt;$invoice_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$filedata&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'reservation_number'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// query 3&lt;/span&gt;
&lt;span class="nv"&gt;$payment_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$filedata&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'payment_date'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;// query 4&lt;/span&gt;
&lt;span class="nv"&gt;$nights_stayed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$filedata&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'number_nights_stayed'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// query 5&lt;/span&gt;
&lt;span class="nv"&gt;$price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$filedata&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'total_revenue'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;          &lt;span class="c1"&gt;// query 6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Six. Six times. &lt;code&gt;$filedata::all()&lt;/code&gt;. The same query. &lt;code&gt;SELECT * FROM imported_files&lt;/code&gt;. Six full round trips to the database before the loop has even started. Six times I loaded the entire table into memory, grabbed one column off it, and threw the rest away, and then immediately did it again.&lt;/p&gt;

&lt;p&gt;The correct code is one query followed by five collection operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$filedata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ImportedFile&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// once. just once. please.&lt;/span&gt;
&lt;span class="nv"&gt;$num_copies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$filedata&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$guest_names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$filedata&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'guest_name'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. That's the whole fix. Load it once. It stays in memory. Use it five times. The database does not need to hear from you six times about the same table in the same second. The database is not a service counter. You don't need to take a new number each time.&lt;/p&gt;

&lt;p&gt;If the database could file a complaint it would have. It did not, because databases are more professional than this code deserved.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Room Number Is Random. I Need You To Understand That The Room Number Is Random.
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$room&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Room"&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;" "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am going to say it plainly and I need you to receive it plainly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The room number is randomly generated.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a hotel invoice generator. Its entire reason for existing is to produce legally adjacent documents that guests may use for expense reimbursement, business travel records, or billing disputes. A guest stays in Room 7. They receive an invoice. The invoice says Room 11. They ask for a corrected invoice. We regenerate it. It says Room 4. We regenerate it again. Room 17.&lt;/p&gt;

&lt;p&gt;There is no floor. There is no ceiling on the chaos. There is only &lt;code&gt;rand(1, 19)&lt;/code&gt;, spinning eternally, indifferent to consequences, assigning rooms with the energy of a hotel that has completely given up on the concept of room assignments as a coherent system.&lt;/p&gt;

&lt;p&gt;The data is there, in the uploaded file. The actual room number exists. It is a column. It was imported. It is sitting in the database. And instead of retrieving it, past me looked at the data, decided &lt;code&gt;rand(1, 19)&lt;/code&gt; captured the spirit of the thing, and moved on.&lt;/p&gt;

&lt;p&gt;My senior's comment on this line was: "the room number is random."&lt;/p&gt;

&lt;p&gt;Not a question. Not a fix suggestion. Just a statement. An observation delivered by someone who had clocked what they were looking at, accepted it, and decided that documentation was the only appropriate response.&lt;/p&gt;

&lt;p&gt;I replied: "yes I'll fix it."&lt;/p&gt;

&lt;p&gt;He replied: "ok."&lt;/p&gt;

&lt;p&gt;That was the entire conversation. We both agreed, silently, that there was nowhere productive to go from there.&lt;/p&gt;




&lt;h2&gt;
  
  
  Nine Queries Per Guest. Inside The Loop. Per Guest. Nine. Per Guest. Inside The Loop.
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nv"&gt;$num_copies&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$_guestNameRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'guest_name'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'A21'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$_invoiceNumRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'invoice_number'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'B15'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$_dateRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'payment_date'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'B17'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// six more of these&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nine. &lt;code&gt;$config-&amp;gt;where('id', 1)&lt;/code&gt;. Nine times. Per iteration. Inside the loop.&lt;/p&gt;

&lt;p&gt;This is the same row. It is always the same row. It was the same row on the first iteration. It will be the same row on the hundredth iteration. The config table does not update between guests. The laws of physics have not changed. The database did not go anywhere. The row is still there, completely unchanged, waiting patiently to be fetched for the 847th time.&lt;/p&gt;

&lt;p&gt;100 guests: 906 total queries. And that's on top of the 6 we fired before the loop even started, which we haven't forgiven yet.&lt;/p&gt;

&lt;p&gt;The fix is to fetch the config once. Before the loop. Assign it to a variable. Use that variable. The database would send a fruit basket. The database would cry. Not from sadness. From relief.&lt;/p&gt;

&lt;p&gt;My senior left his longest comment here. Four sentences. Query optimization. N+1 problems. Eager loading. The works. It was generous. It was thorough. It was the kind of comment you leave when you genuinely believe the person on the other side can be better.&lt;/p&gt;

&lt;p&gt;I read it. I understood approximately 60% of it. I said "thanks, will fix."&lt;/p&gt;

&lt;p&gt;The PR was closed the next day. Not merged. Closed.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;$message_index&lt;/code&gt;, a Variable That Means &lt;code&gt;$i - 1&lt;/code&gt; and Wants You to Know It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$message_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The loop starts at &lt;code&gt;1&lt;/code&gt;. Arrays start at &lt;code&gt;0&lt;/code&gt;. We need &lt;code&gt;$i - 1&lt;/code&gt;. This is fine. This is correct. I have no complaints about the arithmetic.&lt;/p&gt;

&lt;p&gt;What I have complaints about is the name.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$message_index&lt;/code&gt;. In a hotel invoice generator. There are no messages. There has never been a message. No message is sent, received, composed, parsed, or gestured at anywhere in this codebase. But the variable needed a name, and the name it received was &lt;code&gt;$message_index&lt;/code&gt;, with the full confidence of someone who is naming things based on vibes and hoping nobody asks follow-up questions.&lt;/p&gt;

&lt;p&gt;It's not wrong. It functions. But it's like naming a file &lt;code&gt;document1_FINAL_v3_USE_THIS_ONE_actually_final.xls&lt;/code&gt;. Technically a name. Technically communicating something. Not the something it thinks it's communicating.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Ghost of PHP 4, Given the Keys to a Laravel Application
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Content-Type: application/zip"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Content-Disposition: attachment; filename=&lt;/span&gt;&lt;span class="nv"&gt;$zip_name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Content-Length: "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;filesize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;storage_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app/'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$zip_name&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="nb"&gt;readfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;storage_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app/'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$zip_name&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are inside a Laravel controller. Laravel has a response system. It has &lt;code&gt;response()-&amp;gt;download()&lt;/code&gt;. It is one line. It is documented. It handles headers automatically. It does not require you to manually compute &lt;code&gt;Content-Length&lt;/code&gt;. It does not require you to call &lt;code&gt;readfile()&lt;/code&gt; like you're writing a blog post about PHP in 2005. And it absolutely does not require you to call &lt;code&gt;exit&lt;/code&gt; to kill the entire PHP process when you're done.&lt;/p&gt;

&lt;p&gt;But we didn't do that. Instead we reached back through time, found PHP 4, handed it a zip file, said "you know what to do," watched it do a little dance with raw &lt;code&gt;header()&lt;/code&gt; calls, and then &lt;code&gt;exit&lt;/code&gt;ed out of existence.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;exit&lt;/code&gt;. Not &lt;code&gt;return&lt;/code&gt;. &lt;code&gt;exit&lt;/code&gt;. We terminated the process. We didn't return a response. We didn't let Laravel wrap up gracefully. We just... left. Walked out mid-conversation. The framework stood there, holding the door open, waiting for a response object that was never coming.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Final Tally
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;things that went wrong:
- import() that exists to not import:                                     ✅
- DB::rollBack() haunting a transaction that was never born:              ✅
- if block built $file perfectly then watched it die immediately:         ✅
- SELECT * fired 6 times on the same table, consecutively, on purpose:    ✅
- 9 config queries per guest inside the loop, 100 guests = 906 total:     ✅
- room numbers: rand(1, 19). not the actual room. a random room. always:  ✅
- $message_index: means $i-1, works nowhere, explains nothing:            ✅
- exit. just exit. in a Laravel controller. in the year of our lord:      ✅

PR comments left by senior:                                               7
PR comments addressed:                                                    0
PR status:                                                           closed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Verdict
&lt;/h2&gt;

&lt;p&gt;This code never shipped. The PR was closed. My senior reviewed every line of it, the phantom rollback, the six identical queries, the &lt;code&gt;rand(1, 19)&lt;/code&gt; that could have ruined an expense report, all of it — and he left real comments with real explanations, like someone who looked at this disaster and somehow still saw a person who could learn.&lt;/p&gt;

&lt;p&gt;He didn't laugh in the thread. He didn't forward it to anyone. He didn't bring it up in the next sprint. We agreed, without ever agreeing, to never speak of it, the way you agree to never speak of certain things that happen at work parties, certain things that happen on difficult deployments, certain things you open in a PR at 11am on a Tuesday and close by lunch.&lt;/p&gt;

&lt;p&gt;But here is what I think about, now that I am far enough away from it to think:&lt;/p&gt;

&lt;p&gt;He reviewed it anyway. That's the part that stays with me. He could have closed it in thirty seconds. He didn't. He went through it. He explained the N+1 problem to someone who didn't fully understand it yet. He noted the rollback. He pointed at the room number, stated plainly that it was random, and waited.&lt;/p&gt;

&lt;p&gt;That's not a small thing. That is, in fact, the whole thing.&lt;/p&gt;

&lt;p&gt;If you're a senior reading this: I see you. I know what it costs. Every comment you write on a PR from someone still figuring it out is a small act of faith that they will eventually figure it out. That faith is not nothing.&lt;/p&gt;

&lt;p&gt;If you're a junior reading this: somewhere there is a folder. Not a literal one, maybe. But your senior has a folder. Full of PRs that keep them up. You are in that folder. The random room numbers are in that folder.&lt;/p&gt;

&lt;p&gt;Do better. They're rooting for you. They reviewed the whole thing. They always do.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>php</category>
      <category>laravel</category>
    </item>
    <item>
      <title>Episode 1: I Was a Junior Developer and I Must Be Stopped</title>
      <dc:creator>Adam - The Developer</dc:creator>
      <pubDate>Tue, 31 Mar 2026 04:15:12 +0000</pubDate>
      <link>https://dev.to/adamthedeveloper/episode-1-i-was-a-junior-developer-and-i-must-be-stopped-c0m</link>
      <guid>https://dev.to/adamthedeveloper/episode-1-i-was-a-junior-developer-and-i-must-be-stopped-c0m</guid>
      <description>&lt;p&gt;We all start somewhere.&lt;/p&gt;

&lt;p&gt;Some developers begin their careers writing clean, well-structured code, carefully following best practices, naming variables properly, and writing tests like responsible adults.&lt;/p&gt;

&lt;p&gt;Others start by shipping a few bugs here and there, learning, improving, and slowly developing their own style.&lt;/p&gt;

&lt;p&gt;And then there's me.&lt;/p&gt;

&lt;p&gt;Today, we are reviewing a function I wrote years ago that is somehow still running in production, untouched, unbothered, and — against all odds — still working. No one has dared to refactor it. No one has tried to rewrite it. It has achieved something most code never will:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It became too scary to change.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This function is called &lt;code&gt;multipleUpdate&lt;/code&gt;.&lt;br&gt;
It lives in a Laravel application.&lt;br&gt;
It updates multiple items.&lt;br&gt;
I think.&lt;/p&gt;

&lt;p&gt;Here it is, in all its glory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;multipleUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kt"&gt;Scenario&lt;/span&gt; &lt;span class="nv"&gt;$scenario&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$r_arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;intval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;right_arr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$id_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;item_number&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nv"&gt;$r_arr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$name_ja_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name_ja&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$name_en_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name_en&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$type_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;$added&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt; &lt;span class="c1"&gt;// final res&lt;/span&gt;
    &lt;span class="nb"&gt;array_push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$added&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$r_arr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$difference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;array_push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$difference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$id_array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;$id_array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$difference&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;array_push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$added&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$r_arr&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nv"&gt;$difference&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$added&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$scenario&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'item_number'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$added&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$scenario&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'item_number'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$added&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="s1"&gt;'name_ja'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$name_ja_array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;default_name_ja&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'name_en'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$name_en_array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;default_name_en&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'kind'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$type_array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;default_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$scenario&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="s1"&gt;'scenario_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$scenario&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'item_number'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$added&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'name_ja'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$name_ja_array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'name_en'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$name_en_array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'kind'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$type_array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take a moment. Let it wash over you. I'll be here.&lt;/p&gt;

&lt;p&gt;Now let's go through it together, line by line, like a crime scene investigator who is also crying.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Mysterious &lt;code&gt;$r_arr&lt;/code&gt; Variable
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// oh cool, a variable named $r_arr. what does r stand for? right? raw? random?&lt;/span&gt;
&lt;span class="c1"&gt;// who the hell knows. the author was apparently too busy to leave a single hint.&lt;/span&gt;
&lt;span class="nv"&gt;$r_arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;intval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;right_arr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;$r_arr&lt;/code&gt;. Short for &lt;code&gt;right_arr&lt;/code&gt;, which comes from the request field &lt;code&gt;right_arr&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What is "right"? Right as in &lt;em&gt;correct&lt;/em&gt;? Right as in &lt;em&gt;direction&lt;/em&gt;? Right as in the author just started typing and committed before their brain caught up?&lt;/p&gt;

&lt;p&gt;We will never know. There are no comments. There is no documentation. There is only &lt;code&gt;$r_arr&lt;/code&gt;, staring back at you, completely unashamed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sir, This Is an Int
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sir this is an int. you are about to count() and loop over a single goddamn int.&lt;/span&gt;
&lt;span class="c1"&gt;// i genuinely hope you tested this. i genuinely fear that you did.&lt;/span&gt;
&lt;span class="nv"&gt;$id_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;item_number&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nv"&gt;$r_arr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The variable is called &lt;code&gt;$id_array&lt;/code&gt;. It implies an array. It promises an array. It has the word &lt;em&gt;array&lt;/em&gt; right there in the name.&lt;/p&gt;

&lt;p&gt;The fallback is &lt;code&gt;$r_arr&lt;/code&gt;, which we just established is an &lt;code&gt;int&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So if &lt;code&gt;$request-&amp;gt;item_number&lt;/code&gt; is missing, &lt;code&gt;$id_array&lt;/code&gt; is a single integer, and we are about to call &lt;code&gt;count()&lt;/code&gt; on it, loop over it, and index into it like it's an array.&lt;/p&gt;

&lt;p&gt;PHP, famously lenient, will try its best. PHP will not succeed.&lt;/p&gt;




&lt;h2&gt;
  
  
  The &lt;code&gt;?? null&lt;/code&gt; Epidemic
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// breaking news: local developer discovers that $request-&amp;gt;name_ja ?? null&lt;/span&gt;
&lt;span class="c1"&gt;// and $request-&amp;gt;name_ja are the same fucking thing. more at 11.&lt;/span&gt;
&lt;span class="nv"&gt;$name_ja_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name_ja&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$name_en_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name_en&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// still doing it. unbelievable.&lt;/span&gt;
&lt;span class="nv"&gt;$type_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// i am losing my mind.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;$x ?? null&lt;/code&gt; means: "if &lt;code&gt;$x&lt;/code&gt; is null, use... null."&lt;/p&gt;

&lt;p&gt;This is the coding equivalent of a safety net made of holes. It is null, wearing a seatbelt, driving into a wall.&lt;/p&gt;

&lt;p&gt;The author wrote this three times in a row. On three separate lines. Without stopping to wonder if something had gone wrong in their life. &lt;/p&gt;




&lt;h2&gt;
  
  
  The Comment That Explains Nothing
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$added&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt; &lt;span class="c1"&gt;// final res&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"final res."&lt;/p&gt;

&lt;p&gt;Final result? Final response? Final resignation letter? &lt;/p&gt;

&lt;p&gt;You cannot write &lt;code&gt;// final res&lt;/code&gt; and then disappear like a deadbeat dad. Come back. Explain yourself. We deserve that much.&lt;/p&gt;

&lt;p&gt;Also: &lt;code&gt;array_push($added, $r_arr)&lt;/code&gt; to append a single value. The author could have written &lt;code&gt;$added[] = $r_arr&lt;/code&gt;. It's shorter. It's faster. It doesn't make me feel things.&lt;/p&gt;

&lt;p&gt;But they didn't. They used &lt;code&gt;array_push&lt;/code&gt;. For one value. And then kept going.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Round Trip to Nowhere
&lt;/h2&gt;

&lt;p&gt;Now we reach the heart of it. The crown jewel. The part that made me go outside and stand in a field for ten minutes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// oh no. oh hell no. i see where this is going and i am grabbing you by the shoulders.&lt;/span&gt;
&lt;span class="c1"&gt;// you're going to calculate the gaps between IDs, aren't you.&lt;/span&gt;
&lt;span class="c1"&gt;// you absolute disaster of a human being.&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;array_push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$difference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$id_array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;$id_array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// and NOW you're adding them back together. you took [10, 12, 15],&lt;/span&gt;
&lt;span class="c1"&gt;// blew it up into [2, 3], and then painstakingly rebuilt [10, 12, 15].&lt;/span&gt;
&lt;span class="c1"&gt;// you did a round trip to absolutely nowhere and burned CPU cycles for the privilege.&lt;/span&gt;
&lt;span class="c1"&gt;// i am genuinely asking: are you okay? do you need water?&lt;/span&gt;
&lt;span class="c1"&gt;// did someone hurt you?&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$difference&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;array_push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$added&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$r_arr&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nv"&gt;$difference&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let me walk you through what's happening here.&lt;/p&gt;

&lt;p&gt;We have an array of IDs: &lt;code&gt;[10, 12, 15]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Loop one: compute the &lt;em&gt;differences&lt;/em&gt; between consecutive IDs. &lt;code&gt;[2, 3]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Loop two: add those differences back, cumulatively, to reconstruct... &lt;code&gt;[10, 12, 15]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We took the array. We destroyed it. We rebuilt it. We are exactly where we started.&lt;/p&gt;

&lt;p&gt;This is a mathematical round trip that accomplishes zero. The only thing it changes is &lt;code&gt;$r_arr&lt;/code&gt;, which started life as a request input, briefly became a loop accumulator, and is now something else entirely.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$r_arr&lt;/code&gt; has had more identity changes in one function than most people have in a lifetime. Leave &lt;code&gt;$r_arr&lt;/code&gt; alone. &lt;code&gt;$r_arr&lt;/code&gt; has been through enough.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Database Loop of Sadness
&lt;/h2&gt;

&lt;p&gt;We are now in loop three. The database loop. God help us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$added&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$scenario&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'item_number'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$added&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$scenario&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'item_number'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$added&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$scenario&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two database queries per item. One to check if it exists. One to update or create it.&lt;/p&gt;

&lt;p&gt;No transaction wrapper. No bulk operation. Just a prayer and a dream that nothing fails halfway through and leaves the database looking like it was updated by a raccoon who found a keyboard.&lt;/p&gt;

&lt;p&gt;And then there's this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$scenario&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'scenario_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$scenario&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'item_number'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$added&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mf"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;$scenario-&amp;gt;id ?? null&lt;/code&gt;. We dependency-injected &lt;code&gt;$scenario&lt;/code&gt; into the function signature. Laravel loaded it, validated it, handed it to us on a silver platter. If &lt;code&gt;$scenario-&amp;gt;id&lt;/code&gt; is null at this point, &lt;code&gt;?? null&lt;/code&gt; is not saving us. Nothing is saving us. Say your goodbyes.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$added[$i] ?? null&lt;/code&gt;. We are &lt;em&gt;inside the loop that iterates over &lt;code&gt;$added&lt;/code&gt;&lt;/em&gt;. &lt;code&gt;$added[$i]&lt;/code&gt; is guaranteed to exist. The &lt;code&gt;?? null&lt;/code&gt; is not a safety net. It is a tiny umbrella in a drink that is already on fire.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Method That Was Right There the Whole Time
&lt;/h2&gt;

&lt;p&gt;Here is the part that gets me.&lt;/p&gt;

&lt;p&gt;Laravel has had &lt;code&gt;updateOrCreate()&lt;/code&gt; since version 5.3.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$scenario&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;updateOrCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'item_number'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$added&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'name_ja'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$name_ja_array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;default_name_ja&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'name_en'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$name_en_array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;default_name_en&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'kind'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$type_array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;default_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One method. One query. Handles both cases. No manual existence check. No separate create/update branch.&lt;/p&gt;

&lt;p&gt;It was sitting right there the whole time, like a golden retriever waiting by the door.&lt;/p&gt;

&lt;p&gt;And I looked it in the face, said "no thanks," and wrote a three-loop differential equation instead.&lt;/p&gt;

&lt;p&gt;I'm not angry at myself.&lt;/p&gt;

&lt;p&gt;I'm disappointed.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(I am absolutely furious.)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Final Tally
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;crimes committed:
- 3 loops to rebuild the array we already had:                       ✅
- up to 2N db queries with zero transaction safety:                  ✅
- $r_arr living three different lives in one function:               ✅
- ?? null deployed purely as emotional support:                      ✅
- updateOrCreate() left completely untouched like a jilted lover:    ✅
- "final res" as a comment explaining nothing:                       ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And yet.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;And yet.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It works. It has always worked. It is running in production right now, at this very moment, as you read this. No one has touched it. No one will. It works and that is the most infuriating part of all of this.&lt;/p&gt;




&lt;p&gt;The real &lt;code&gt;multipleUpdate&lt;/code&gt; was the trust issues we developed along the way.&lt;/p&gt;

&lt;p&gt;If you have code like this living rent-free in your production environment — code that works, code that you are afraid to touch, code that you wrote and no longer recognize — I want you to know: you are not alone.&lt;/p&gt;

&lt;p&gt;We all have a &lt;code&gt;multipleUpdate&lt;/code&gt;. Some of us just haven't found ours yet.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Episode 2 coming whenever I'm emotionally ready.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>career</category>
      <category>php</category>
    </item>
    <item>
      <title>Resume-Driven Development Is Quietly Killing Your Product</title>
      <dc:creator>Adam - The Developer</dc:creator>
      <pubDate>Wed, 25 Mar 2026 06:51:34 +0000</pubDate>
      <link>https://dev.to/adamthedeveloper/the-code-that-builds-careers-and-breaks-products-1kje</link>
      <guid>https://dev.to/adamthedeveloper/the-code-that-builds-careers-and-breaks-products-1kje</guid>
      <description>&lt;p&gt;There is a conversation that happens in engineering teams all over the world, constantly, and it almost always goes the same way.&lt;/p&gt;

&lt;p&gt;Someone proposes a new project. Before the requirements are even clear, before anyone has talked to a user, before the scope has been defined, someone opens their mouth and says: "What if we used this for the Rust rewrite?" Or: "This could be a great chance to try out that new edge runtime." Or: "I've been wanting to get some GraphQL experience."&lt;/p&gt;

&lt;p&gt;And just like that, the technical decision has been made. Not by the problem. By someone's LinkedIn headline in 18 months.&lt;/p&gt;

&lt;p&gt;This is Resume-Driven Development. It is everywhere. Almost nobody talks about it honestly. Partly because it's awkward to name, and partly because most of the people who should be naming it are also doing it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What It Actually Looks Like
&lt;/h2&gt;

&lt;p&gt;RDD rarely announces itself. It doesn't walk into the room wearing a sandwich board. It hides inside reasonable-sounding language, nodding along to the business requirements before quietly steering the conversation toward whatever technology someone bookmarked last weekend.&lt;/p&gt;

&lt;p&gt;"We need something more scalable." Scalable to what? You have 200 users. Postgres hasn't broken a sweat. What you actually need is three more customers, not a distributed event-driven microservices architecture that requires four new AWS services, a dedicated DevOps hire, and someone brave enough to own the on-call rotation.&lt;/p&gt;

&lt;p&gt;"The developer experience with this new tool is so much better." For whom? For the one engineer who spent two weekends learning it and is now the team's only expert? That is not better DX. That is a bus factor of one wearing a conference lanyard.&lt;/p&gt;

&lt;p&gt;"We should use what the industry is moving toward." The industry is moving toward approximately forty things simultaneously and half of them will be deprecated before you finish the migration. Bun, Deno, whatever JavaScript framework launched last Tuesday, that new database that promises to replace all other databases while also making coffee. The industry is always moving. Products still need to ship.&lt;/p&gt;

&lt;p&gt;The giveaway is always the same: the technology choice happens before the problem is fully understood. The cart is so far in front of the horse it has its own Kubernetes cluster.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Honest Reason This Happens
&lt;/h2&gt;

&lt;p&gt;Let's just say it plainly. The job market for developers rewards people who have used the new thing more than it rewards people who deeply understand boring, proven things. A resume with Kubernetes, GraphQL, and a trendy new runtime gets more recruiter messages than a resume that says "kept a Rails monolith running smoothly for five years and scaled it to a million users."&lt;/p&gt;

&lt;p&gt;The incentives are completely misaligned with good engineering.&lt;/p&gt;

&lt;p&gt;So developers, who are rational people, optimize for what the market rewards. They look for projects where they can justify using the new thing. They advocate for the new thing in architecture discussions. They get the experience on paper. Then they move on, often right around the time the true cost of that decision starts showing up in sprint retrospectives.&lt;/p&gt;

&lt;p&gt;The product is not their primary concern in that moment. Their career is. That is human. That is understandable. It is also quietly catastrophic for the companies on the receiving end who wonder why everything takes so long and costs so much.&lt;/p&gt;




&lt;h2&gt;
  
  
  RDD Is Not the Actual Problem
&lt;/h2&gt;

&lt;p&gt;Here is where most essays like this go wrong: they treat RDD as the villain. It is not.&lt;/p&gt;

&lt;p&gt;Wanting to grow your skills is healthy. Wanting to work with modern tools is healthy. Advocating for better technology on your team is healthy. If developers never pushed to try new things, we would all still be writing SOAP XML by hand and arguing about tabs versus spaces in Vim. Some of us would enjoy that. Most of us would not.&lt;/p&gt;

&lt;p&gt;The real problem is not that someone wanted to use a new tool. The real problem is that nobody stopped to have an honest conversation about what the business actually needed before the new tool got greenlit.&lt;/p&gt;

&lt;p&gt;RDD becomes toxic at exactly the moment when the technology choice gets made in a vacuum. When nobody asked "what are we trying to accomplish in the next six months?" When nobody checked whether the existing stack had actually failed or was merely unfashionable. When the pitch was "this is better" without anyone defining better for whom, by what measure, over what time horizon.&lt;/p&gt;

&lt;p&gt;A team that wants to use Rust and has a genuine performance problem that Rust would solve? Great. Go forth and rewrite. A team that wants to use Rust because a senior engineer just finished a side project in it and the current service is written in Go and works perfectly fine? That is RDD doing damage in a fleece vest and acting like it's doing the company a favor.&lt;/p&gt;

&lt;p&gt;The difference is a conversation. A specific, slightly uncomfortable conversation where someone asks "what does the business need right now" and everyone has to sit with the answer even if it is boring.&lt;/p&gt;




&lt;h2&gt;
  
  
  What It Costs When That Conversation Doesn't Happen
&lt;/h2&gt;

&lt;p&gt;The costs of undiscussed RDD are almost never measured because they happen slowly and diffusely and get attributed to other causes.&lt;/p&gt;

&lt;p&gt;A team picks a new framework because it is exciting and nobody pushed back with requirements. Six months later, onboarding takes twice as long because the framework has steep learning curves and opinionated patterns that new hires have to unlearn their previous experience to adopt. That cost does not get traced back to the framework choice. It gets called "hiring is hard."&lt;/p&gt;

&lt;p&gt;A team builds on a cutting-edge but immature library because it looked great at the time and the business case was hand-wavy. Two years later, the maintainer abandons it. Now the team owns it. That cost shows up as "tech debt" in a planning meeting, orphaned from the original decision that created it.&lt;/p&gt;

&lt;p&gt;A team introduces a microservices architecture to a product that did not need one because someone wanted the experience. Local development becomes a nightmare involving Docker Compose files that nobody fully understands. Debugging a request means tracing it across six services. Deployments are complicated. None of this gets credited to the architectural decision. It gets called "complexity" as though complexity is weather that just rolls in and there is nothing to be done about it.&lt;/p&gt;

&lt;p&gt;The engineers who made those choices are often long gone by the time the costs fully materialize. They have the experience on their resumes. The company has the mess. And a new round of engineers eager to rewrite it all in something more modern.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Boring Stack Works
&lt;/h2&gt;

&lt;p&gt;There is a version of this essay that collapses into a "just use Rails" post and I want to resist that because it misses the point. The point is not that new technology is bad. The point is that the decision should follow from the problem, and following from the problem requires first understanding the problem, which requires talking to the people who have the problem.&lt;/p&gt;

&lt;p&gt;Boring, mature, well-understood technology has compounding advantages that are consistently undervalued because they are invisible. You do not see the bugs that did not happen because the ORM is stable. You do not see the hours not spent reading documentation for a library that has fifteen years of Stack Overflow answers covering every possible mistake. You do not see the hires who got productive in two weeks instead of two months because they already knew the stack.&lt;/p&gt;

&lt;p&gt;Boring technology has been debugged by thousands of teams in thousands of situations. The edge cases are documented. The failure modes are known. When something breaks at 3am, someone has already written the blog post about it and the fix is four lines.&lt;/p&gt;

&lt;p&gt;You are paying the price of novelty whether you account for it or not. The question is whether you made that tradeoff deliberately or just kind of drifted into it because the senior engineer seemed really enthusiastic in that one architecture meeting.&lt;/p&gt;




&lt;h2&gt;
  
  
  When New Technology Is Actually the Right Call
&lt;/h2&gt;

&lt;p&gt;There are real situations where reaching for something new is the correct engineering decision. When the problem genuinely cannot be solved well with mature tools. When performance requirements are extreme and you have actual benchmarks showing existing tools fail. When the new technology has enough production adoption that its failure modes are understood rather than theoretical. When the team has the bandwidth to absorb the learning cost without stalling product delivery.&lt;/p&gt;

&lt;p&gt;The difference in all these cases is that the technology choice follows from a requirement. You started with a real constraint. You evaluated options against it. You chose the best fit.&lt;/p&gt;

&lt;p&gt;That is engineering. It is also, not coincidentally, a much easier decision to defend when things get hard and someone above your pay grade starts asking questions.&lt;/p&gt;

&lt;p&gt;What is not engineering is starting with "I want to use X" and working backward to a justification. What is especially not engineering is doing that without ever asking the product manager, the founder, or the customer what they actually need from the next six months of development.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Conversation That Makes the Difference
&lt;/h2&gt;

&lt;p&gt;The question that almost never gets asked before a major stack decision is embarrassingly simple: what does this business need to be true in twelve months, and how does this technology choice help or hinder that?&lt;/p&gt;

&lt;p&gt;Not "what's interesting." Not "what's scalable in theory." Not "what would be impressive in a conference talk." What does the business need.&lt;/p&gt;

&lt;p&gt;That question is boring to ask. It often produces boring answers. A monolith. A managed database. Server-rendered HTML. A framework your entire team already knows. Things that do not make for interesting blog posts but do make for products that ship on time and do not require a tribal knowledge handoff every time someone quits.&lt;/p&gt;

&lt;p&gt;The engineering leader's job, in part, is to make sure this conversation happens before the exciting choice gets made. That means creating space for someone to say "we want to try this, and here is how it maps to what we actually need" and requiring that the second half of that sentence be filled in with something real, not vibes.&lt;/p&gt;

&lt;p&gt;If someone can genuinely make the case that the new tool serves the business, then by all means use the new tool. Put it on the roadmap. Put it on your resume. You earned it honestly. The goal was never to ban ambition. The goal was to make sure ambition and business reality are at least on speaking terms before you start the sprint.&lt;/p&gt;




&lt;p&gt;The new framework will not save your product. The exciting database will not fix your architecture. The Rust rewrite will not make your users happier.&lt;/p&gt;

&lt;p&gt;But a team that openly discusses what they want to learn, weighs it honestly against what the business needs, and makes a deliberate choice? That team can pick almost anything and make it work.&lt;/p&gt;

&lt;p&gt;The technology was never really the point. The conversation was.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>career</category>
    </item>
  </channel>
</rss>
