<?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: Doogal Simpson</title>
    <description>The latest articles on DEV Community by Doogal Simpson (@doogal).</description>
    <link>https://dev.to/doogal</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%2F3657111%2Fac69c96a-33e1-4023-99ef-ee3059b3ccb6.jpeg</url>
      <title>DEV Community: Doogal Simpson</title>
      <link>https://dev.to/doogal</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/doogal"/>
    <language>en</language>
    <item>
      <title>Why QA Engineers Are Your Team's Invisible Safety Net</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Wed, 02 Sep 2026 16:59:12 +0000</pubDate>
      <link>https://dev.to/doogal/why-qa-engineers-are-your-teams-invisible-safety-net-2jk1</link>
      <guid>https://dev.to/doogal/why-qa-engineers-are-your-teams-invisible-safety-net-2jk1</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR: QA engineers operate in a tough paradox: when they do their job perfectly, their work is invisible. However, their role is incredibly challenging, requiring them to constantly deliver difficult feedback to defensive developers. To build resilient software, we must recognize QA as a collaborative safety net rather than an obstacle.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Think about the air conditioning in a large office building. When it works perfectly, nobody thinks about it. You sit at your desk, comfortable, completely focused on your work. But the moment it breaks? Everyone is furious, demanding to know who is responsible. &lt;/p&gt;

&lt;p&gt;Quality Assurance (QA) engineers live in that exact same reality. They are the invisible infrastructure of a software team. When they do their job exceptionally well, the product simply works, and they get zero credit. But when something slips through to production, all eyes turn to them. &lt;/p&gt;




&lt;h2&gt;
  
  
  Why does QA feel invisible until everything breaks?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;When quality assurance operates at peak efficiency, the end-user experiences a seamless product, which can mistakenly lead management to believe the QA role is redundant. The true value of QA is only realized when they are gone and critical bugs slip directly into production.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine a hypothetical scenario where a team is under pressure to ship a new subscription billing system. To save time, management decides to bypass the QA phase and deploy directly. Within hours, edge-case database locks begin failing, charging users twice. &lt;/p&gt;

&lt;p&gt;Without QA, we lose our safety net. A great tester doesn't just click buttons; they map out the weird, dark corners of your logic that you didn't have time to think about. When everything runs smoothly, it isn't because the code was perfect from day one—it is because QA caught the cracks before the concrete dried.&lt;/p&gt;




&lt;h2&gt;
  
  
  How do we handle the friction of QA bug reports?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The natural friction between developers and QA exists because a tester's job is to deliver bad news to engineers who are emotionally invested in their work. Reframing bug reports as a collaborative tool rather than a personal critique is key to a healthy engineering culture.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's be honest: nobody likes being told their baby is ugly. When you spend three days writing an elegant algorithm, hearing 'this doesn't work' can immediately make you defensive. But we have to separate our self-worth from our code. QA is not trying to catch you failing; they are trying to prevent the team from failing in front of the customer.&lt;/p&gt;

&lt;p&gt;Here is how we can reframe these interactions to build a better working relationship:&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;The Defensive Reaction&lt;/th&gt;
&lt;th&gt;The Collaborative Response&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;QA finds an edge-case race condition&lt;/td&gt;
&lt;td&gt;'That is an unrealistic scenario; nobody will do that.'&lt;/td&gt;
&lt;td&gt;'Great catch. Let's look at how we can handle that lock state safely.'&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QA rejects a ticket due to UI misalignment&lt;/td&gt;
&lt;td&gt;'It is close enough. We have more important things to ship.'&lt;/td&gt;
&lt;td&gt;'Thanks for spotting that mismatch. Let me tweak the CSS to match the designs.'&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QA asks for more API documentation&lt;/td&gt;
&lt;td&gt;'The code is self-documenting; just read the PR.'&lt;/td&gt;
&lt;td&gt;'I will update the Swagger spec to make this easier for you to test.'&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  How can developers build a stronger partnership with QA?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Developers can build a better partnership by involving QA early in the development cycle, sharing context before writing code, and respecting their expertise. Treating QA as peer engineers rather than a final gatekeeper reduces friction and improves software quality.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you only hand things over to QA ten minutes before a release deadline, you are setting everyone up for failure. Instead, try these three practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Shift Left:&lt;/strong&gt; Invite your QA engineers to the initial design and RFC reviews. They will spot logical flaws in your planning before you write a single line of code.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Write Testing Notes:&lt;/strong&gt; When you submit a pull request, don't just write 'done.' Explain how you built it, what components were affected, and where you think the weak points might be.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Celebrate the Saves:&lt;/strong&gt; When a QA engineer catches a critical bug before a release, celebrate it publicly in your team chat. Normalize the idea that catching bugs is a massive win for the entire team.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why can't automated tests completely replace manual QA?
&lt;/h3&gt;

&lt;p&gt;Automated tests only verify that the software behaves exactly as the developer programmed it to behave. They cannot evaluate the actual user experience, spot logical inconsistencies in the product design, or perform the creative, exploratory testing that a skilled human QA engineer does.&lt;/p&gt;

&lt;h3&gt;
  
  
  How should we handle disagreements over what is considered a bug?
&lt;/h3&gt;

&lt;p&gt;When a developer and a tester disagree on a bug, the focus should shift away from technicalities and toward the user. Ask: 'Does this behavior harm the user experience or trust?' If the answer is yes, it is a bug. If it remains ambiguous, bring in the product manager to clarify the requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does involving QA early actually save development time?
&lt;/h3&gt;

&lt;p&gt;Finding a bug during the design phase costs almost nothing to fix. Finding a bug during development takes hours of rewrite. Finding a bug in production can cost thousands of dollars and damage company reputation. Involving QA early prevents bugs before they are even built.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>testing</category>
      <category>qualityassurance</category>
      <category>devops</category>
    </item>
    <item>
      <title>Handling Partial Failures in Distributed Systems</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Tue, 01 Sep 2026 15:01:39 +0000</pubDate>
      <link>https://dev.to/doogal/handling-partial-failures-in-distributed-systems-49d1</link>
      <guid>https://dev.to/doogal/handling-partial-failures-in-distributed-systems-49d1</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick Answer: To handle partial failures in distributed systems, avoid complex distributed transactions. Instead, design for the "unhappy path" using an orchestration pattern (like the Saga pattern). This ensures that if a downstream service fails after a payment is processed, compensating actions automatically roll back the transaction and refund the user.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's be honest: a lot of the code running in production right now is built on a prayer. I've watched too many developers spend weeks writing elegant features, only to completely ignore what happens when the network inevitably hiccups. They design exclusively for the "happy path" and treat system failures as a minor afterthought.&lt;/p&gt;

&lt;p&gt;But here is the reality of distributed systems: the network is unreliable, APIs crash, and validation fails. If you do not actively write code to handle these failures, you will eventually leave your data in a corrupted state, which usually means taking a customer's money and giving them nothing in return. &lt;/p&gt;

&lt;h2&gt;
  
  
  Why does the "happy path" fallacy break distributed systems?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The happy path fallacy is the assumption that every API call, database write, and network request in a sequence will succeed. When developers write linear code without handling intermediate failures, it leads to inconsistent system states—like charging a customer without delivering their purchase.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I cannot tell you the number of times I've seen systems written in such a way where the code takes the money, sits in a middle server, and then calls a downstream server to deliver the product. If that downstream call fails, you are stuck in the absolute worst-case scenario: you have the customer's money, but they have no product. &lt;/p&gt;

&lt;p&gt;Some developers might joke that keeping the money without delivering the product is a win, but I guarantee you it is the fastest way to get your engineering team into deep trouble.&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 dangerous "Happy Path" approach I see all the time&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;checkout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Take the money&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;paymentService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&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;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Network drop? Validation failure?&lt;/span&gt;
  &lt;span class="c1"&gt;// If this fails, the user is charged but gets no inventory!&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;inventoryService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reserve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&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;h2&gt;
  
  
  Why should I avoid distributed transactions (2PC)?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Distributed transactions, such as Two-Phase Commit (2PC), force services to lock databases until all nodes agree on a change. This tight coupling destroys system throughput, increases latency, and introduces a single point of failure that defeats the purpose of microservices.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When developers realize their linear code is fragile, their first instinct is often to reach for distributed transactions. I strongly advise against this. Distributed transactions require database locks across service boundaries. If one of your services experiences a network lag or goes down mid-transaction, your entire system grinds to a halt while holding those locks open. It simply does not scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I design a resilient rollback workflow?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Rather than relying on database locks, use an orchestration-based Saga pattern to manage distributed state. An orchestrator tracks each step of a multi-service workflow and automatically triggers compensating actions (like refunds) if a downstream step fails.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of trying to prevent failures with database locks, I build systems that accept failure as an inevitability. I recommend using an orchestration system. An orchestrator is a resilient state machine that coordinates the workflow steps. If a downstream step fails, the orchestrator detects the failure and executes compensating actions—like issuing a refund—to roll back the entire workflow to a clean state.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;th&gt;Latency&lt;/th&gt;
&lt;th&gt;Coupling&lt;/th&gt;
&lt;th&gt;Failure Handling&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Two-Phase Commit (2PC)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Tight (database level)&lt;/td&gt;
&lt;td&gt;Automated rollback, but blocks resources&lt;/td&gt;
&lt;td&gt;Monoliths, single DBs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Choreographed Saga&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Loose (event-driven)&lt;/td&gt;
&lt;td&gt;Complex to trace, relies on event chains&lt;/td&gt;
&lt;td&gt;Simple, decentralized workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Orchestrated Saga&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Loose (central controller)&lt;/td&gt;
&lt;td&gt;Explicit compensating steps (rollbacks)&lt;/td&gt;
&lt;td&gt;Complex business transactions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do I test unhappy paths in my local development environment?
&lt;/h3&gt;

&lt;p&gt;I suggest using chaos engineering tools or configuring mock downstream APIs to return random timeouts, &lt;code&gt;500 Internal Server Error&lt;/code&gt; responses, or validation failures. If your orchestrator does not automatically trigger a compensating rollback during these simulated network drops, your recovery logic is broken.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a compensating transaction?
&lt;/h3&gt;

&lt;p&gt;A compensating transaction is an explicit action designed to undo the effects of a previous, successful step in a workflow. For example, if step one charges a card and step two fails to allocate warehouse stock, the compensating transaction is an API call that issues a refund to the customer's card.&lt;/p&gt;

&lt;h3&gt;
  
  
  What tools should I use for workflow orchestration?
&lt;/h3&gt;

&lt;p&gt;I highly recommend looking at dedicated workflow engines like Temporal, Camunda, or AWS Step Functions. These platforms allow you to define resilient state machines that handle retries, timeouts, and compensating rollbacks out of the box, saving you from writing custom, fragile error-handling boilerplate.&lt;/p&gt;

</description>
      <category>distributedsystems</category>
      <category>softwareengineering</category>
      <category>microservices</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Stop Terminal Prompting: A Markdown-First AI Workflow</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Mon, 31 Aug 2026 08:27:52 +0000</pubDate>
      <link>https://dev.to/doogal/stop-terminal-prompting-a-markdown-first-ai-workflow-1ple</link>
      <guid>https://dev.to/doogal/stop-terminal-prompting-a-markdown-first-ai-workflow-1ple</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick Answer:&lt;/strong&gt; Relying on interactive terminal prompts for slow AI agents forces us into constant, exhausting context-switching. To reclaim your flow state, don't code in the terminal. Instead, draft your thoughts, specs, and feedback in a local Markdown file first, using it as an offline scratchpad to keep your thinking deliberate.&lt;/p&gt;




&lt;p&gt;Is anyone else's brain just completely fried? &lt;/p&gt;

&lt;p&gt;Seriously. If you've been spending most of your week working with autonomous developer agents like Claude Code or Aider, you know exactly what I'm talking about. &lt;/p&gt;

&lt;p&gt;You kick off a task, the agent spins up, and it's doing its thing. While it's spending five or ten minutes running, you think, "Well, I should probably be doing something productive." So you open a new tab and start on another task. &lt;/p&gt;

&lt;p&gt;Before you know it, you've got six or seven different things on the go at once. It's like spinning plates, and the constant context-switching is absolutely exhausting. We've traded our old "compiling" coffee break for a frantic, reactive feedback loop. Here's how I broke out of that trap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do AI agents make us feel so exhausted?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Working with autonomous agents is draining because their slow runtimes tempt us to multitask, which completely destroys our flow state. Instead of thinking deeply, we get stuck in a reactive loop of chasing terminal outputs across six different tabs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine you're building a complex schema migration. In the past, you wrote the migration, ran the test suite, and had a clear, linear path of thought. With an agent, you write a quick prompt, and then you wait. That waiting period is where the trouble starts.&lt;/p&gt;

&lt;p&gt;Because the terminal is interactive, we treat it like a real-time chat app. We feel the urge to respond immediately to whatever partial solution or gibberish the agent spits back. This constant disruption prevents us from entering a true flow state. We stop being software architects and become reactive prompt managers.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does a "Markdown-first" workflow solve agent fatigue?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A Markdown-first workflow replaces frantic terminal prompting with a slow, local scratchpad inside your IDE. Instead of reacting instantly to whatever the agent spits out, you brain-dump your thoughts into a file first, letting you process everything at your own pace.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You treat the markdown file as a buffer. You write down what you want to achieve, analyze what the agent did, and draft your corrections offline before the agent even sees them. &lt;/p&gt;

&lt;p&gt;Here's an example of the scratchpad structure I keep open in my editor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Refactoring JWT Auth Middleware&lt;/span&gt;

&lt;span class="gu"&gt;## Agent Output Analysis&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; The agent successfully updated the token validation.
&lt;span class="p"&gt;-&lt;/span&gt; It missed handling the expired token database cleanup.

&lt;span class="gu"&gt;## Next Prompt Instructions&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; Keep the current middleware logic.
&lt;span class="p"&gt;2.&lt;/span&gt; Add an automated cron cleanup function in &lt;span class="sb"&gt;`cron/cleanup.go`&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By keeping your thoughts in a file, you don't lose your place when you inevitably have to step away or wait for a long agent execution run.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you transition from terminal prompting to Markdown files?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Stop typing complex prompts directly into your CLI. Write them in a local &lt;code&gt;scratchpad.md&lt;/code&gt; file first, then feed that file to your agent when you're ready, keeping your focus inside your IDE and away from terminal-induced panic.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This approach fundamentally changes how you interact with AI. Here's how the two workflows stack up:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Terminal-First Prompting&lt;/th&gt;
&lt;th&gt;Markdown-First Prompting&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pacing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fast, reactive, and urgent&lt;/td&gt;
&lt;td&gt;Deliberate, offline, and controlled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cognitive Load&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High (constant context-switching)&lt;/td&gt;
&lt;td&gt;Low (single source of truth)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Focus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fragile (distracted by execution logs)&lt;/td&gt;
&lt;td&gt;Deep (focused on system architecture)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Output Quality&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Messy, iterative trial-and-error&lt;/td&gt;
&lt;td&gt;Structured, high-context instructions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Writing things down gives your brain the space to actually think about the engineering problem rather than just reacting to the screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do I feed the Markdown file back to the AI agent?
&lt;/h3&gt;

&lt;p&gt;Most modern agent CLIs allow you to pass files as context. You can either reference the markdown file directly in your command (like &lt;code&gt;claude-code -f scratchpad.md&lt;/code&gt;), or simply copy and paste your structured "Next Instructions" section straight into the prompt when the agent finishes its run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Doesn't writing Markdown slow down the development process?
&lt;/h3&gt;

&lt;p&gt;Yes, it slows you down, and that's precisely the point. By forcing yourself to write a couple of paragraphs explaining your thoughts before running a command, you avoid sending the agent down hallucinated rabbit holes that take thirty minutes to debug.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I commit these Markdown scratchpads to git?
&lt;/h3&gt;

&lt;p&gt;I don't recommend it. I add &lt;code&gt;*.scratchpad.md&lt;/code&gt; or a dedicated &lt;code&gt;.prompts/&lt;/code&gt; directory to my global &lt;code&gt;.gitignore&lt;/code&gt;. These files are meant to be temporary, highly personal cognitive buffers to help you think through a task, not permanent project documentation.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>developerproductivity</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Build a Scale-Proof Distributed Counter</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Mon, 31 Aug 2026 08:24:32 +0000</pubDate>
      <link>https://dev.to/doogal/how-to-build-a-scale-proof-distributed-counter-127b</link>
      <guid>https://dev.to/doogal/how-to-build-a-scale-proof-distributed-counter-127b</guid>
      <description>&lt;p&gt;&lt;strong&gt;To build a scale-proof like counter, you must decouple user actions from database writes. Start with optimistic UI updates for instant feedback, log the event to a user-specific store, and process counts asynchronously using in-memory distributed buffers before periodically flushing aggregated totals to a persistent database.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If I'm building a weekend side project, a single SQL &lt;code&gt;UPDATE&lt;/code&gt; statement is fine. But at YouTube scale—where millions of users hit the like button simultaneously across the globe—that simple write becomes a massive architectural bottleneck. &lt;/p&gt;

&lt;p&gt;Here is how I would design a distributed system that handles this kind of write-heavy throughput without melting the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why can't I update the database immediately on every click?
&lt;/h2&gt;

&lt;p&gt;Direct database updates fail at scale because high-volume concurrent writes to the same database row cause severe lock contention. To handle millions of writes per second, I must shield the database using an asynchronous, distributed processing layer.&lt;/p&gt;

&lt;p&gt;Let's say a viral video goes live. Within seconds, ten thousand users hit the like button. If I try to execute &lt;code&gt;UPDATE videos SET likes = likes + 1 WHERE id = 123&lt;/code&gt; ten thousand times simultaneously, the database has to lock that specific row for each transaction. This creates a massive queue, drives CPU usage to 100%, and eventually crashes the database.&lt;/p&gt;

&lt;p&gt;To solve this, I decouple the user interface from the system of record. When you click that like button, the UI updates instantly. The application doesn't wait for a database roundtrip; it optimistically assumes success and increments the counter locally on your screen. &lt;/p&gt;

&lt;h2&gt;
  
  
  How do I design a system to handle millions of likes at scale?
&lt;/h2&gt;

&lt;p&gt;To scale high-throughput counters, I split the operation into two parallel pipelines: a user-centric ledger and a video-centric aggregated counter. This decouples the identity tracking ("who liked what") from the high-frequency numerical aggregation ("how many likes total").&lt;/p&gt;

&lt;p&gt;Once the client emits the request, I split the backend work into two independent flows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;The User-Centric Flow&lt;/strong&gt;: I log the specific relationship (e.g., "User X liked Video Y"). This data is sharded horizontally by User ID, making it highly distributed and easy to scale. It ensures that if you refresh the page, I can look up your history and show that you already liked the video.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The Video-Centric Flow&lt;/strong&gt;: I emit an asynchronous event—"User X liked Video Y"—into an ingestion pipeline to update the overall count. This pipeline is built to ingest a wild, constant stream of events without slowing down.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pipeline Component&lt;/th&gt;
&lt;th&gt;Data Responsibility&lt;/th&gt;
&lt;th&gt;Scale Strategy&lt;/th&gt;
&lt;th&gt;Storage Medium&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;User-Centric&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tracks specific relationships ("User X liked Video Y").&lt;/td&gt;
&lt;td&gt;Sharded horizontally by User ID.&lt;/td&gt;
&lt;td&gt;Relational DB or Key-Value Store.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Video-Centric&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tracks overall aggregations ("Video Y has N likes").&lt;/td&gt;
&lt;td&gt;Distributed in-memory counters &amp;amp; batching.&lt;/td&gt;
&lt;td&gt;In-memory cache (Redis/Custom) -&amp;gt; Persistent DB.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How do I implement distributed in-memory counters?
&lt;/h2&gt;

&lt;p&gt;Distributed in-memory counters ingest a stream of event messages across multiple parallel processes to track counts in local memory. A periodic background worker then sweeps these in-memory nodes, aggregates the counts, and flushes them to persistent storage in a single, batched database transaction.&lt;/p&gt;

&lt;p&gt;Instead of hitting the database for every single event, I route the incoming stream of like events across multiple memory processes. Let's say I have five instances of a counter service running. When a "like" event occurs, it is routed to one of these instances, which increments an in-memory counter for that video. &lt;/p&gt;

&lt;p&gt;Every few seconds, a background process runs to reconcile these numbers. It reads all the distributed, in-memory counters across the processes, sums them up, and flushes the aggregated count to the persistent database. If a video receives 10,000 likes in three seconds across the fleet, the database experiences only a single write operation of &lt;code&gt;+10,000&lt;/code&gt; instead of 10,000 individual writes. &lt;/p&gt;

&lt;p&gt;This is how I balance real-time user experiences with backend storage limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is an optimistic UI update and why is it used for likes?
&lt;/h3&gt;

&lt;p&gt;An optimistic UI update is a frontend pattern where the client-side UI immediately reflects a successful state (e.g., highlighting the like button and incrementing the count) before the server confirms the action. I use it to ensure a fast, responsive user experience despite the asynchronous nature of the backend queue.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do distributed counters handle eventual consistency?
&lt;/h3&gt;

&lt;p&gt;Because distributed counters rely on in-memory buffering and periodic database flushing, the public-facing like count is eventually consistent. While you will immediately see your own like, other users will see the total count update in small jumps every few seconds as background processes reconcile and write the batched counts to persistent storage.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens to the counter if an in-memory node crashes?
&lt;/h3&gt;

&lt;p&gt;If a node crashes before flushing, some in-flight counts can be lost. To prevent this, I run durable, append-only event streams (like Apache Kafka) behind the ingestion layer. If a worker node goes down, a new node can replay unprocessed events from the stream to reconstruct the correct counts before writing them to the database.&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>softwareengineering</category>
      <category>backend</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>Why AI Coding Can't Speed Up B2B Sales Cycles</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Sun, 30 Aug 2026 13:04:11 +0000</pubDate>
      <link>https://dev.to/doogal/why-ai-coding-cant-speed-up-b2b-sales-cycles-53en</link>
      <guid>https://dev.to/doogal/why-ai-coding-cant-speed-up-b2b-sales-cycles-53en</guid>
      <description>&lt;p&gt;&lt;strong&gt;Modern development tools and AI have supercharged our ability to ship code, but they haven't changed the speed of human decision-making. High-ticket B2B sales still require weeks or months to build trust, meaning your overall business velocity is still gated by human relationships, not your deployment pipeline.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine you just built a revolutionary B2B product. Using LLMs and modern frameworks, you and a small team designed, coded, and deployed the entire system in a single weekend. It is blazing fast, highly scalable, and ready for production. &lt;/p&gt;

&lt;p&gt;But the moment you try to sell it to an enterprise client, your momentum hits a brick wall. &lt;/p&gt;

&lt;p&gt;A colleague of mine recently summarized this paradox perfectly during a lunch-and-learn: &lt;strong&gt;"Computers got faster; humans didn't."&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Even if AI allows us to build software at lightspeed, we are still selling that software to human beings. And humans do not operate at the speed of silicon.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why hasn't AI shortened the B2B sales cycle?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;AI cannot automate the human trust required to close high-value business deals. When an enterprise buyer signs off on a software contract worth tens of thousands of dollars, they are risking their own professional reputation, which requires human-to-human relationship building.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you are selling a $10-a-month browser extension, self-serve checkout works fine. But when the price tag jumps to five or six figures, the buying dynamic changes entirely. The enterprise buyer has a set of existential questions that no marketing page or AI agent can fully resolve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Will this integration break our existing legacy systems?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;If this software goes down at 3:00 AM on a holiday, who is going to help us fix it?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Is this startup still going to exist in two years?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Will adopting this tool get me fired if it fails?&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Answering these questions takes time. It requires multiple meetings, product demonstrations, and technical deep dives. Trust is a slow-burning currency that must be earned; it cannot be generated by a prompt.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is the real bottleneck in B2B software adoption?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The primary bottleneck in enterprise sales is not code production, but institutional hurdles like security reviews, legal redlining, and political alignment. While engineers can push updates in seconds, legal teams and information security (InfoSec) departments still operate on human schedules.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To understand where the time actually goes, look at the friction points in a typical enterprise procurement lifecycle:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Process Phase&lt;/th&gt;
&lt;th&gt;Operational Velocity&lt;/th&gt;
&lt;th&gt;Primary Bottleneck&lt;/th&gt;
&lt;th&gt;AI Impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Code Generation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Extremely High&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;High (Massive speedup)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Security &amp;amp; Compliance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;InfoSec questionnaires, SOC2 audits&lt;/td&gt;
&lt;td&gt;Low (Still requires human sign-off)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Legal &amp;amp; Procurement&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Master Service Agreements, legal redlining&lt;/td&gt;
&lt;td&gt;Minimal (Lawyers must review liability)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trust &amp;amp; Relationships&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Extremely Low&lt;/td&gt;
&lt;td&gt;Executive consensus, political risk&lt;/td&gt;
&lt;td&gt;None (Purely human relationship)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;An LLM can generate a compliance policy in seconds, but it cannot force a corporate legal team to approve a liability clause any faster. The bottleneck has simply shifted entirely from engineering to operations.&lt;/p&gt;




&lt;h2&gt;
  
  
  How can engineering teams adapt to slow sales cycles?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Engineers must shift their focus from raw feature velocity to building "trust-enabling" technical assets that make the human sales process frictionless. This means prioritizing robust security, clear documentation, and dead-simple proof-of-concept setups over endless new feature development.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your sales team takes six months to close a deal, shipping ten new features this week won't help them close it any faster. Instead, use your engineering cycles to remove the technical friction points that slow down human decision-makers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Build a Self-Service Sandbox:&lt;/strong&gt; Allow prospective buyers to play with your product in a safe, isolated environment without needing a complex setup process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automate Compliance Readiness:&lt;/strong&gt; Make it easy for your sales team to hand over SOC2 reports, penetration testing results, and architecture diagrams.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplify Data Portability:&lt;/strong&gt; Build clear import and export pipelines. Buyers are more likely to trust you if they know they can easily get their data back out if the relationship doesn't work.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At the end of the day, shipping code is only half the battle. If we want our businesses to move faster, we have to stop treating sales as an afterthought and start engineering our products to build trust by design.&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can AI agents eventually automate the B2B sales process?
&lt;/h3&gt;

&lt;p&gt;While AI can automate lead generation, personalized outreach, and initial scheduling, it cannot automate the final stages of B2B sales. High-value transactions require accountability, and buyers will always want a human account executive to hold responsible for delivery and support.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does the human bottleneck impact startup runway?
&lt;/h3&gt;

&lt;p&gt;Startups often burn through their capital because founders assume fast development cycles will translate to fast revenue. If you build your product in three months, you must still budget for a six-to-nine-month enterprise sales cycle before that product generates significant cash flow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should developers join sales calls to speed up the process?
&lt;/h3&gt;

&lt;p&gt;Yes. Having a technical engineer on a call can dramatically cut down the sales cycle. Developers can answer complex architecture and security questions on the spot, bypassing weeks of back-and-forth email chains between the buyer's IT department and your sales team.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>saas</category>
      <category>ai</category>
      <category>startup</category>
    </item>
    <item>
      <title>Fix AI Agent Jargon with Simplified Technical English</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Thu, 27 Aug 2026 12:36:00 +0000</pubDate>
      <link>https://dev.to/doogal/fix-ai-agent-jargon-with-simplified-technical-english-3lb8</link>
      <guid>https://dev.to/doogal/fix-ai-agent-jargon-with-simplified-technical-english-3lb8</guid>
      <description>&lt;p&gt;&lt;strong&gt;Tired of Claude Code generating bizarre, overly dramatic jargon like "load-bearing spine"? You can fix this by enforcing Simplified Technical English (STE) in your system instructions or &lt;code&gt;.claudemd&lt;/code&gt; files. This 1970s aerospace standard restricts vocabulary, forcing your AI agent to communicate in clear, direct, and highly actionable prose.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;"The load-bearing spine has hit a ceiling, and that is a significant foot gun with a large blast radius."&lt;/p&gt;

&lt;p&gt;If you have spent any time recently working with AI coding agents, you have probably stared at your terminal reading absolute gibberish like this, wondering: &lt;em&gt;What on earth are you trying to tell me?&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;I asked a straightforward technical question, and instead of a direct answer, I got a theatrical performance. It is incredibly tiring to translate AI metaphors back into plain English just to figure out which line of code actually broke. &lt;/p&gt;

&lt;p&gt;Fortunately, there is a remarkably elegant fix for this. The solution does not involve complex prompt engineering; instead, it leverages a fifty-year-old aerospace standard: &lt;strong&gt;Simplified Technical English (STE)&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does Claude Code output weird technical jargon?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;AI models generate overly dramatic jargon because they are trained on vast internet corpuses where technical writing is often cluttered, metaphorical, and performative. To sound authoritative, the model indexes on complex vocabulary and metaphorical hand-waving instead of simple, direct statements.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine a scenario where your team is debugging a database lock. A human engineer would say, "The transaction is blocked." An AI model, eager to please and sound sophisticated, might describe it as a "temporal execution bottleneck causing systemic architectural paralysis."&lt;/p&gt;

&lt;p&gt;This happens because reinforcement learning from human feedback (RLHF) often rewards models for sounding smart and comprehensive. Without strict stylistic constraints, the agent defaults to verbose, metaphorical explanations that add cognitive load rather than solving your problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Simplified Technical English (STE) and how does it help?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Simplified Technical English (originally ASD-STE100) is an international writing standard developed in the 1970s for aerospace maintenance manuals to ensure complex procedures were clear and unambiguous. It reduces grammatical complexity and restricts vocabulary so that there is only one word for one meaning.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the 1970s, aerospace engineers realized that overly complex manuals were leading to maintenance errors. They created STE to strip away ambiguity. By applying these same constraints to your AI agent, you force it to stop using metaphors and stick to direct, literal descriptions.&lt;/p&gt;

&lt;p&gt;Here is how standard AI-speak compares to Simplified Technical English:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;AI-Generated Jargon&lt;/th&gt;
&lt;th&gt;Simplified Technical English (STE) Equivalent&lt;/th&gt;
&lt;th&gt;Why it works better&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"The load-bearing spine has hit a performance ceiling."&lt;/td&gt;
&lt;td&gt;"The main database query is slow because CPU usage is at 100%."&lt;/td&gt;
&lt;td&gt;It identifies the exact component and issue without metaphors.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"This architectural change has a massive blast radius."&lt;/td&gt;
&lt;td&gt;"This change affects multiple dependent services."&lt;/td&gt;
&lt;td&gt;It provides a clear risk assessment without dramatic language.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"We need to orchestrate a paradigm shift in our caching layer."&lt;/td&gt;
&lt;td&gt;"We must change how the cache works."&lt;/td&gt;
&lt;td&gt;It is direct, simple, and actionable.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  How do I configure my AI agent to use Simplified Technical English?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;You can enforce STE by injecting style rules directly into your agent's system configuration files, such as &lt;code&gt;.claudemd&lt;/code&gt;, &lt;code&gt;developer_instructions.md&lt;/code&gt;, or your global AI memory settings. Once these rules are in place, the model will automatically filter its reasoning into clean, structured prose.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you are using Claude Code or similar terminal-based agents, create or edit your project's instructions file and append this brief system prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Rules for Output Style:&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Write all responses in Simplified Technical English (STE).
&lt;span class="p"&gt;-&lt;/span&gt; Use short, active sentences (under 20 words).
&lt;span class="p"&gt;-&lt;/span&gt; Avoid metaphors, idioms, and dramatic jargon (e.g., "blast radius", "foot gun").
&lt;span class="p"&gt;-&lt;/span&gt; Use only one verb per sentence to describe actions.
&lt;span class="p"&gt;-&lt;/span&gt; Do not use abstract nouns when concrete nouns are available.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By establishing these boundaries, you bypass the theatrical fluff. The agent stops trying to write a software engineering blog post in your terminal and starts acting like a highly efficient technical documentation tool.&lt;/p&gt;

&lt;h3&gt;
  
  
  FAQ
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Does forcing STE limit the AI's ability to solve complex technical problems?
&lt;/h4&gt;

&lt;p&gt;No. Restricting the output language to STE does not degrade the model's reasoning capabilities. It only changes how the model formats and explains the solution, stripping away confusing analogies while keeping the technical logic intact.&lt;/p&gt;

&lt;h4&gt;
  
  
  Where should I put these STE instructions for tools like Claude or Cursor?
&lt;/h4&gt;

&lt;p&gt;You should place these instructions in your project's root configuration files. For Claude Code, use &lt;code&gt;.claudemd&lt;/code&gt; or your custom developer instructions. For Cursor or VS Code Copilot, paste the rules into your system prompt or &lt;code&gt;.cursorrules&lt;/code&gt; file.&lt;/p&gt;

&lt;h4&gt;
  
  
  What are the core rules of Simplified Technical English I should enforce?
&lt;/h4&gt;

&lt;p&gt;The core rules are to keep sentences under 20 words, restrict verbs to active voice, use only one meaning per word (e.g., use "show" instead of "demonstrate" or "reveal"), and strictly ban metaphors and slang.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>promptengineering</category>
      <category>claudecode</category>
      <category>developertools</category>
    </item>
    <item>
      <title>How YouTube Scales Watch History Using Bigtable</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Wed, 26 Aug 2026 14:27:35 +0000</pubDate>
      <link>https://dev.to/doogal/how-youtube-scales-watch-history-using-bigtable-1a9b</link>
      <guid>https://dev.to/doogal/how-youtube-scales-watch-history-using-bigtable-1a9b</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR: YouTube scales watch history for billions of users using Google Bigtable. Instead of running expensive SQL query sorts on the fly, they design a massive, sorted key-value store where the row keys are structured as UserID plus a Reverse Timestamp. This allows YouTube to fetch a user's most recent videos instantly via a high-performance prefix scan.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you ask me, one of the coolest things about YouTube is clicking on your history page and seeing your recently watched videos load instantly. It feels like a trivial feature. But when I see developers talk about building this at scale—dealing with billions of active users writing and reading at the exact same time—they often overlook how quickly a traditional database will choke. You can't just throw a standard SQL database at this problem unless you want to melt your servers and leave your users staring at a loading spinner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does SQL fail at YouTube-scale watch history?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;When I see engineers struggle with scaling history feeds, they usually try to optimize SQL queries that just aren't meant for this volume. Relational databases fail at this scale because sorting billions of rows on the fly using indexes is incredibly CPU-heavy. Under massive concurrent write-and-read loads, keeping index trees balanced while scanning them causes severe database locking.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's say we have a traditional SQL table tracking watch events. Every time a user watches a video, we append a row. When they want to see their last ten videos, the database has to search the index, sort those specific records by timestamp in descending order, and return the top ten. &lt;/p&gt;

&lt;p&gt;Doing this for billions of users watching videos simultaneously is like trying to rebuild an airplane engine mid-flight. The database simply cannot keep up with the index maintenance and the sorting overhead. If you try to run this under real-world load, your database will quickly fall over.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Google Bigtable and how does it store data?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Google Bigtable is a distributed, NoSQL database designed to handle massive workloads by functioning like a giant, lexicographically sorted map. Instead of supporting complex relational joins, it stores data as simple key-value pairs sorted alphabetically by their row keys.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;From what I've seen, if you want absolute speed at scale, you have to sacrifice relational flexibility—there's no way around it. Bigtable doesn't do joins, and it doesn't do complex query execution plans. Instead, it acts as a massive, distributed, sorted map.&lt;/p&gt;

&lt;p&gt;Because the keys are pre-sorted, if you scan a range of keys, Bigtable reads them sequentially off the storage medium. This means if we can design our keys correctly, we can guarantee that a user's history is stored together, pre-sorted, and ready to read in a single disk seek.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a reverse timestamp and how does it solve sorting?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;To me, the cleverest part of this entire design is how they avoid sorting altogether using a reverse timestamp. A reverse timestamp is simply the maximum possible epoch integer value minus the current epoch timestamp, which naturally forces newer records to the top of an alphabetical index.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's look at how the math works. In a normal timeline, larger numbers represent more recent events. But in an alphabetically sorted map, we want the most recent events to appear first. By subtracting the current time from a fixed maximum, a newer, larger timestamp yields a smaller reverse timestamp. &lt;/p&gt;

&lt;p&gt;When Bigtable sorts these keys alphabetically, the smallest keys naturally rise to the top—meaning your absolute newest watch history is always at the very beginning of your data range. Here is how you generate this key structure:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateHistoryKey&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;timestampMs&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;MAX_EPOCH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9223372036854775807&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 64-bit max integer&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reverseTimestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;MAX_EPOCH&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nc"&gt;BigInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timestampMs&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;`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="nx"&gt;reverseTimestamp&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;h2&gt;
  
  
  How does a prefix scan retrieve watch history instantly?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A prefix scan allows the database to locate the starting boundary of a user's data instantly and read only the first few sequential rows. Because the row keys are pre-sorted in reverse chronological order, the database simply reads the first 10 rows and terminates the query.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's look at how Bigtable stores this on disk for a hypothetical user, &lt;code&gt;user_123&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Row Key&lt;/th&gt;
&lt;th&gt;Video ID&lt;/th&gt;
&lt;th&gt;Actual Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;user_123#9223372036854575807&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vid_999&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3 seconds ago (Most Recent)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;user_123#9223372036854675807&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vid_888&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2 minutes ago&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;user_123#9223372036854775807&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vid_777&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1 hour ago&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To fetch the history, the application queries Bigtable for keys starting with the prefix &lt;code&gt;user_123#&lt;/code&gt; with a limit of 10. Bigtable jumps directly to the first key starting with &lt;code&gt;user_123#&lt;/code&gt;, reads the first 10 rows sequentially, and returns them. No sorting, no heavy index scanning, and absolutely no loading spinners.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can you use reverse timestamps in relational databases like PostgreSQL?
&lt;/h3&gt;

&lt;p&gt;While you can physically store reverse timestamps in a relational database, it is generally unnecessary. Relational databases use B-Tree indexes which can be scanned backward just as quickly as forward. Reverse timestamps are a design pattern optimized specifically for distributed, NoSQL key-value stores that rely heavily on forward-only prefix scanning.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens when the maximum epoch timestamp is reached?
&lt;/h3&gt;

&lt;p&gt;If you use a standard 64-bit integer as your maximum epoch value, the maximum timestamp is so far in the future (the year 292,277,026,596) that the Earth's sun will have run out of hydrogen before your keys roll over. For all practical software applications, the limit is functionally infinite.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why not use an in-memory cache like Redis for each user's history?
&lt;/h3&gt;

&lt;p&gt;While Redis is incredibly fast, keeping the entire watch history of billions of users in-memory is prohibitively expensive. Bigtable balances the high write-throughput of an in-memory system with the cost-effective storage of persistent disk, pulling data blocks into memory only when they are actively queried.&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>nosql</category>
      <category>database</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Why You Should Wait 3 Months for Open-Source AI</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Wed, 26 Aug 2026 14:23:22 +0000</pubDate>
      <link>https://dev.to/doogal/why-you-should-wait-3-months-for-open-source-ai-nab</link>
      <guid>https://dev.to/doogal/why-you-should-wait-3-months-for-open-source-ai-nab</guid>
      <description>&lt;p&gt;&lt;strong&gt;Stop chasing the bleeding edge of proprietary AI models. By waiting just three months to adopt new technology, you allow cheaper, highly stable open-source alternatives to catch up in intelligence. This pragmatic lag avoids buggy releases, lowers infrastructure costs, and frees you from supporting restrictive vendor ecosystems.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’ve realized recently that I tend to play video games about two years after they actually launch. It’s not some grand, calculated plan—I’m just busy, and I don't get around to them right away. But this accidental delay has some massive advantages. By the time I buy a game, it's usually on sale, the game-breaking bugs have been patched, and the exhausting hype cycle is long dead. I get a stable, polished experience for a fraction of the cost.&lt;/p&gt;

&lt;p&gt;It turns out, this exact same patient approach is the smartest way to handle AI models.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should you always use the latest frontier AI models?
&lt;/h2&gt;

&lt;p&gt;No, you should not default to the newest frontier models for production applications. While bleeding-edge models offer high benchmarks, they are expensive, prone to unannounced behavioral shifts, and lock you into proprietary ecosystems. &lt;/p&gt;

&lt;p&gt;Imagine you are building an automated classification system. If you jump on the newest proprietary model on day one, you are paying peak API prices. You are also at the mercy of rate limits, sudden API deprecations, and undocumented model updates that can silently break your prompts. &lt;/p&gt;

&lt;p&gt;By stepping back from the absolute leading edge, you bypass this chaotic testing phase. You let other teams spend their time and budget finding the bugs, while you focus on building stable, predictable software.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the benefits of waiting for open-source AI models?
&lt;/h2&gt;

&lt;p&gt;Waiting a few months allows the open-source community to release models that match the raw intelligence of yesterday's proprietary giants. This short delay grants you total control over your data privacy, hosting costs, and infrastructure.&lt;/p&gt;

&lt;p&gt;Instead of paying a premium to a closed-source provider, a short wait allows you to run an equivalent open-weights model on your own terms. Here is how the trade-offs look when you step off the daily AI treadmill:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Day 1 (Frontier)&lt;/th&gt;
&lt;th&gt;Month 3 (Open Source)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Premium API rates per token&lt;/td&gt;
&lt;td&gt;Pennies via self-hosting or commodity providers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High risk of model drift, bugs, and downtime&lt;/td&gt;
&lt;td&gt;Stable, frozen weights under your direct control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Autonomy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tied to a single vendor's API and data policies&lt;/td&gt;
&lt;td&gt;Freedom to host anywhere and protect user privacy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Is it really okay to be three months behind the AI curve?
&lt;/h2&gt;

&lt;p&gt;Yes, staying three months behind the curve is completely fine because the performance gap between frontier models and open-source models has shrunk to almost nothing. In that tiny window, you gain pricing leverage and the freedom to avoid supporting organizations whose policies you dislike.&lt;/p&gt;

&lt;p&gt;Frontier labs move fast, but they also bring a lot of baggage—including shifting usage terms, restrictive licensing, and high costs. If you have valid gripes with how these closed-source companies operate, you don't have to support them. By lagging just slightly behind the frontier, you can bypass them entirely. You get the same level of intelligence on your own hardware, free from vendor politics and restrictive commercial terms.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Do I lose a competitive advantage by not using the newest AI models?
&lt;/h3&gt;

&lt;p&gt;Rarely. Most end-users care about speed, reliability, and accuracy, not which specific model runs under the hood. A slightly older, highly optimized open-source model running with low latency often provides a far better user experience than a slow, bleeding-edge proprietary model.&lt;/p&gt;

&lt;h3&gt;
  
  
  How quickly do open-source models catch up to proprietary ones?
&lt;/h3&gt;

&lt;p&gt;The gap has closed dramatically. Open-source models like Llama and Mistral regularly match the benchmark performance of proprietary frontier models within a three-to-six-month window, making long-term reliance on expensive closed APIs unnecessary.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why is using a slightly older AI model more stable for production?
&lt;/h3&gt;

&lt;p&gt;Older models have thoroughly documented edge cases, established community wrappers, and predictable performance. Because the weights are frozen and often run on your own infrastructure, you eliminate the risk of "model drift," where a remote vendor updates a model and suddenly breaks your application's prompts.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>softwareengineering</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>How AI Automates Fast Decisions in Software Engineering</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Mon, 24 Aug 2026 15:13:07 +0000</pubDate>
      <link>https://dev.to/doogal/how-ai-automates-fast-decisions-in-software-engineering-35ae</link>
      <guid>https://dev.to/doogal/how-ai-automates-fast-decisions-in-software-engineering-35ae</guid>
      <description>&lt;p&gt;&lt;strong&gt;AI is shifting the software engineering landscape by taking over "fast decisions"—the automatic, high-context but low-complexity tasks like running tests and catching syntax errors. This frees up developers to focus entirely on "slow decisions" like system architecture, API boundaries, and product design.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’ve been thinking a lot about the book &lt;em&gt;Thinking, Fast and Slow&lt;/em&gt; lately. The core point of the book is that your brain operates in two distinct modes. When you're walking down the street, your brain is in a fast, automatic mode. You're navigating around other people on autopilot, making decisions without even realizing you're making them. But then you have slow decisions—like choosing which barbecue to buy for your garden. That takes deliberate, conscious thought.&lt;/p&gt;

&lt;p&gt;When I think about how we interact with AI, this is exactly where I see things heading. AI is starting to pick up the fast, automatic decisions we make in our day-to-day engineering jobs, and that is going to fundamentally change how we spend our time.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does Kahneman’s theory apply to writing code?
&lt;/h2&gt;

&lt;p&gt;In software engineering, fast decisions are the low-friction, repetitive tasks we execute on autopilot, like running a test suite or fixing a syntax error. Slow decisions are the deliberate, strategic choices, like architecting a system or deciding on a test coverage strategy. AI is stepping in to run the fast decisions so we can focus entirely on the slow ones.&lt;/p&gt;

&lt;p&gt;For example, when I am writing code, deciding &lt;em&gt;whether&lt;/em&gt; to run the unit tests isn't really a decision I need to make. Of course I should run them. That is a fast decision. But deciding &lt;em&gt;how&lt;/em&gt; those tests should be written, what scenarios we need to cover, and how the overall system should behave—those are slow decisions. &lt;/p&gt;

&lt;p&gt;If we can offload the automatic "yes, run the tests" layer of our workflow, we reclaim the mental space needed for the hard, analytical design work that actually moves a project forward.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the "fast decisions" we can offload to AI?
&lt;/h2&gt;

&lt;p&gt;Fast decisions are deterministic tasks that require local codebase context but very little creative reasoning. When I use a coding agent, I want it to handle things like running unit tests after a change, fixing formatting issues, or cleaning up import statements. These are things we can easily automate once an agent has enough context about our workspace.&lt;/p&gt;

&lt;p&gt;I like to split these developer decisions into two distinct categories based on how much deep thought they require:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Decision Type&lt;/th&gt;
&lt;th&gt;Developer Mode&lt;/th&gt;
&lt;th&gt;Task Examples&lt;/th&gt;
&lt;th&gt;AI Agent Feasibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fast Decisions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Autopilot (System 1)&lt;/td&gt;
&lt;td&gt;Running unit tests, fixing syntax issues, formatting code, basic refactoring.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;High&lt;/strong&gt; (Can be fully automated)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Slow Decisions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deliberate (System 2)&lt;/td&gt;
&lt;td&gt;Designing system architecture, defining test coverage strategy, API design.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Low&lt;/strong&gt; (Requires human reasoning)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How does delegating these decisions change our day-to-day jobs?
&lt;/h2&gt;

&lt;p&gt;Delegating fast decisions to AI keeps us out of the constant loop of minor distractions, allowing us to stay in flow state longer. It means we stop wasting cognitive energy on routine tasks and spend it instead on deep architectural work.&lt;/p&gt;

&lt;p&gt;Let's say you are building a payment processing service. In a traditional workflow, your day is fragmented by micro-decisions. You write some logic, manually trigger a test, fix a linter warning, check a configuration file, and then try to get back to your main train of thought. Every minor system step demands a small action from you.&lt;/p&gt;

&lt;p&gt;If you hand those fast decisions to a coding agent, the agent runs the tests, formats the files, and flags obvious errors in the background. You don't have to decide to do those things; they just happen. This frees you up to think about the actual business logic, concurrency issues, and edge cases. It changes your job entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can AI handle slow decisions in our codebases?
&lt;/h3&gt;

&lt;p&gt;No, because slow decisions require deep context, trade-off analysis, and human-centric design. AI can offer suggestions, but deciding &lt;em&gt;how&lt;/em&gt; a system should scale or &lt;em&gt;what&lt;/em&gt; security model to use still requires human judgement and deep technical expertise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Will this automation make junior developers obsolete?
&lt;/h3&gt;

&lt;p&gt;I don't think so, but it will shift what we expect of them. Instead of spending their first year learning to write boilerplate and debug configuration files, they will need to learn system design and how to orchestrate AI tools much earlier in their careers.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I prepare my codebase for AI automation?
&lt;/h3&gt;

&lt;p&gt;The best way is to make your fast decisions as deterministic as possible. If you have a clean project structure, fast test runs, and clear linting rules, it becomes much easier for an AI agent to step in and make those routine decisions for you.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>ai</category>
      <category>developerproductivity</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Why Toxic Comments are a Systems Scaling Metric</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Sat, 22 Aug 2026 13:25:39 +0000</pubDate>
      <link>https://dev.to/doogal/why-toxic-comments-are-a-systems-scaling-metric-2glc</link>
      <guid>https://dev.to/doogal/why-toxic-comments-are-a-systems-scaling-metric-2glc</guid>
      <description>&lt;p&gt;&lt;strong&gt;Receiving negative comments is an inevitable statistical side effect of scaling your reach. Based on the 90-9-1 engagement rule, an insulting comment is a 1-in-40,000 event that only triggers when your content or software expands past your core audience to fresh, unprimed users. It is a lagging indicator of growth.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every software engineer understands the concept of scaling pain. When you run a service with a hundred users, edge-case bugs rarely trigger. But scale that service to millions of users, and those "one-in-a-million" hardware failures or race conditions start happening every single Tuesday. &lt;/p&gt;

&lt;p&gt;Recently, a particularly toxic comment landed on one of my videos. While it is easy to take these things personally, looking at it through an engineering lens reveals a fascinating statistical truth: hostile feedback is not a personal failure, but a classic scaling problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the 90-9-1 rule in online community engagement?
&lt;/h2&gt;

&lt;p&gt;The 90-9-1 rule dictates that 90% of users consume content silently, 9% actively curate it (likes/shares), and only 1% create content (comments). This power-law distribution governs almost all digital platforms and open-source repositories.&lt;/p&gt;

&lt;p&gt;Imagine you are monitoring traffic on a microservice. Most requests are read-only (the 90% who view), a small fraction write metadata (the 9% who like or share), and a tiny sliver perform heavy write operations (the 1% who comment).&lt;/p&gt;

&lt;p&gt;If your system only handles 1,000 views, you might only see 10 comments in total. At this volume, the probability of encountering extreme outliers is incredibly low. But as your traffic grows, that 1% "write" path begins to generate a statistically representative sample of the entire internet—including the outliers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the probability of receiving a toxic comment in tech?
&lt;/h2&gt;

&lt;p&gt;In technical niches, the probability of receiving an insulting comment is roughly 1-in-40,000 (or 0.0025%) per new visitor. This rate is determined by compounding the 1% comment rate with a 5% negative comment rate, and a further 5% insult rate within that negative subset.&lt;/p&gt;

&lt;p&gt;Not all negative comments are created equal. In software, we generally enjoy a more constructive environment compared to highly polarized niches like politics. Here is how the nested probabilities break down when your reach expands:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Funnel Percentage&lt;/th&gt;
&lt;th&gt;Cumulative Probability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Total Viewers&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1.0000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commenters&lt;/td&gt;
&lt;td&gt;1% of viewers&lt;/td&gt;
&lt;td&gt;0.0100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Negative Comments&lt;/td&gt;
&lt;td&gt;5% of commenters&lt;/td&gt;
&lt;td&gt;0.0005&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insulting/Toxic Comments&lt;/td&gt;
&lt;td&gt;5% of negative comments&lt;/td&gt;
&lt;td&gt;0.000025 (1 in 40,000)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When we filter the negative feedback further, we find a spectrum ranging from constructive criticism (e.g., "this sorting algorithm is suboptimal") to friendly banter (e.g., "found the Vim user") to flat-out insults. The purely toxic insults make up about 5% of that negative 5% slice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do toxic comments indicate your system is scaling?
&lt;/h2&gt;

&lt;p&gt;Toxic comments act as a lagging indicator of reach because they only occur when your distribution engine pushes content to "fresh" user segments. Your core, recurring audience has already warm-cached your value, whereas new cohorts represent unprimed traffic.&lt;/p&gt;

&lt;p&gt;Think of your regular audience as a local cache. They know your style, understand your context, and have high cache hit rates for your content's intent. They are highly unlikely to leave hostile feedback because they have self-selected to be there.&lt;/p&gt;

&lt;p&gt;But when a video or repository starts trending, the algorithm serves it to "cold" users who have no prior context. These are fresh database hits. As these fresh viewers flood in, the 1-in-40,000 probability event is bound to fire. If you get an insulting comment, it means your distribution pipeline is working well enough to reach outside your established network.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ: Frequently Asked Questions about Online Engagement Stats
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How should developers handle negative feedback on open-source projects?
&lt;/h3&gt;

&lt;p&gt;Separate constructive criticism from noise. If a comment points out a performance bottleneck or security flaw, treat it as a bug report. If it is purely abusive, treat it as spam, moderate it immediately, and do not waste cognitive bandwidth on it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does the 90-9-1 rule apply to GitHub repository engagement?
&lt;/h3&gt;

&lt;p&gt;Yes, the same power law applies. The vast majority of developers use a library silently (90%), a smaller portion star or fork it (9%), and a tiny minority open issues, submit pull requests, or participate in discussions (1%).&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the difference between constructive criticism and toxic comments in tech?
&lt;/h3&gt;

&lt;p&gt;Constructive criticism targets the code, architecture, or logic, offering a path toward improvement. Toxic comments target the person, utilizing insults or disparagement without offering any objective engineering value.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>opensource</category>
      <category>developerlife</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Why Scaling Software Is an Organizational Problem</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Fri, 21 Aug 2026 14:41:07 +0000</pubDate>
      <link>https://dev.to/doogal/why-scaling-software-is-an-organizational-problem-43m6</link>
      <guid>https://dev.to/doogal/why-scaling-software-is-an-organizational-problem-43m6</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR: I've realized that scaling software development is fundamentally an organizational coordination problem, not a technical one. Just like multiple authors trying to write cohesive chapters of a single book without a shared plot or language, developers must constantly align on architectures and data contracts to prevent the system from fracturing.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I've spent enough time staring at fractured codebases to know the exact moment a project starts to unravel. It usually looks like a strictly typed Scala microservice trying to talk to a wild-west Node.js service over a fragile, undocumented HTTP interface. I look at this chaos and wonder: why is it so incredibly hard for us to build software that actually works together? Writing code in isolation is dead simple; coordinating hundreds of developers to write the same system is where the wheels fall off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is scaling software development so difficult for large engineering teams?
&lt;/h2&gt;

&lt;p&gt;From what I've observed, scaling software fails because it shifts the challenge from technical execution to a massive organizational coordination problem. As engineering teams grow, maintaining consistent architecture, data contracts, and design patterns across independent modules becomes exponentially harder.&lt;/p&gt;

&lt;p&gt;I look at it through an analogy: imagine you are writing a book with another person. You split the work in half: you write the first half, and they write the second. When you compile the drafts, you discover they wrote their half in French, while you wrote yours in English. &lt;/p&gt;

&lt;p&gt;Even if you both agree on English, you might find that you wrote a hard sci-fi novel while your partner wrote high fantasy. Now multiply this by tens or hundreds of developers. Everyone is writing different "chapters" of the same system. Without continuous, deliberate alignment, you end up with a fragmented product that fails to make sense end-to-end.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do development teams maintain consistency across microservices and modules?
&lt;/h2&gt;

&lt;p&gt;I recommend maintaining consistency by establishing strict, automated boundaries such as API contracts, shared schemas, and standardized local environments. These tools act as the shared editorial guidelines for our collaborative book, ensuring all contributors stay on the same page.&lt;/p&gt;

&lt;p&gt;When I analyze scaling problems, I find it useful to map these collaborative challenges directly to their technical equivalents. Here is how I categorize these friction points and the technical solutions that actually solve them:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Collaborative Book Problem&lt;/th&gt;
&lt;th&gt;Software Engineering Equivalent&lt;/th&gt;
&lt;th&gt;Technical Solution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Writing in different languages&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fragmented tech stacks and incompatible frameworks&lt;/td&gt;
&lt;td&gt;Standardized "golden paths" and approved language lists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Conflicting genres (Sci-Fi vs. Fantasy)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mismatched architectural paradigms (OOP vs. Functional)&lt;/td&gt;
&lt;td&gt;Linters, style guides, and shared RFC processes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Inconsistent character behaviors&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Broken API contracts and unexpected payload changes&lt;/td&gt;
&lt;td&gt;Schema registries (OpenAPI/Protobuf) and contract testing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What is the role of API contracts in team coordination?
&lt;/h2&gt;

&lt;p&gt;I view API contracts as the shared plot points that ensure different authors' chapters connect seamlessly. By defining exactly how data enters and exits a service, teams can build their components independently without breaking the broader system.&lt;/p&gt;

&lt;p&gt;Without these explicit contracts, you get the software equivalent of a character suddenly changing names or teleporting across the world between chapters. When one team modifies a database schema or changes an API payload format without notifying downstream teams, the entire system breaks. &lt;/p&gt;

&lt;p&gt;I always advise enforcing API schemas and running consumer-driven contract tests in your CI/CD pipelines. This ensures that even if developers are writing their chapters in complete isolation, the final product remains cohesive.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do you prevent "too many cooks in the kitchen" in a large codebase?
&lt;/h3&gt;

&lt;p&gt;I suggest defining clear domain boundaries using Domain-Driven Design (DDD) principles. Assigning clear, single-team ownership to specific modules or services reduces the coordination overhead and prevents conflicting changes to the same parts of the codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why do microservices often fail to solve coordination problems?
&lt;/h3&gt;

&lt;p&gt;I've seen microservices fail because they split the codebase physically but do not inherently solve team alignment. Without robust API schemas, automated contract testing, and clear communication channels, microservices simply transform local codebase confusion into complex, distributed system failures.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a "golden path" in software engineering?
&lt;/h3&gt;

&lt;p&gt;In my view, a golden path is an opinionated, pre-packaged set of templates, tools, and best practices provided to developers to build and deploy software. It acts as the formatting template for our collaborative book, making it easy for developers to do the right thing by default.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>architecture</category>
      <category>microservices</category>
      <category>api</category>
    </item>
    <item>
      <title>How Adaptive Bitrate Streaming Works</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Wed, 19 Aug 2026 14:47:11 +0000</pubDate>
      <link>https://dev.to/doogal/how-adaptive-bitrate-streaming-works-ph1</link>
      <guid>https://dev.to/doogal/how-adaptive-bitrate-streaming-works-ph1</guid>
      <description>&lt;p&gt;&lt;strong&gt;Ever wonder why a streaming video suddenly drops to blurry 360p and then instantly snaps back to crisp 1080p? This happens because modern video platforms use Adaptive Bitrate Streaming (ABS). The video is sliced into 2-to-6-second chunks at multiple resolutions, and your player dynamically switches between them based on real-time network speeds.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I have noticed a funny thing when watching tech tutorials online. I will be staring at a screen, and suddenly the code turns into a pixelated mess. A few seconds later, without me touching a single setting or pausing, the image snaps back into crisp, high-definition focus.&lt;/p&gt;

&lt;p&gt;It is easy to assume the creator recorded their screen poorly, but that is not what is happening. I want to talk about the clever web engineering behind this phenomenon: Adaptive Bitrate Streaming (ABS). It is the invisible system powering platforms like YouTube, Netflix, and Twitch.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does adaptive bitrate streaming actually work?
&lt;/h2&gt;

&lt;p&gt;When I look at how Adaptive Bitrate Streaming (ABS) functions, it boils down to slicing a video into multiple resolution tiers and chopping each tier into short, uniform segments. A master manifest file indexes these segments, allowing the player to dynamically request the optimal quality chunk based on active network speed.&lt;/p&gt;

&lt;p&gt;If I upload a video to a modern streaming platform, a background processing pipeline immediately goes to work. First, the platform transcodes my original video file into several distinct resolutions—typically ranging from 360p up to 4K.&lt;/p&gt;

&lt;p&gt;Then, the platform chops each of these resolution tracks into identical, bite-sized segments (usually between two and six seconds long). These chunks align perfectly. If I look at chunk #5 across all resolutions, they all represent the exact same two-second window of time. Finally, the system writes an index file—the manifest—so the playback client knows exactly where to find every single chunk.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Transcoding&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Convert original file into multiple resolutions (e.g., 360p, 720p, 1080p, 4K)&lt;/td&gt;
&lt;td&gt;Prepares alternative assets for varying network speeds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Chunking&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Chop each resolution track into identical 2-6 second segments&lt;/td&gt;
&lt;td&gt;Enables granular, mid-playback quality switching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Indexing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Create a manifest file mapping all chunks and resolutions&lt;/td&gt;
&lt;td&gt;Gives the client-side player a roadmap of available options&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Delivery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Playback engine requests chunks dynamically based on telemetry&lt;/td&gt;
&lt;td&gt;Maintains uninterrupted streaming over fluctuating connections&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Why do videos get pixelated instead of pausing to buffer?
&lt;/h2&gt;

&lt;p&gt;Rather than forcing you to wait while a massive high-definition chunk buffers over a slow connection, the player will proactively grab the next 2-second chunk at a lower resolution. This strategy prioritizes uninterrupted, continuous playback over visual fidelity, keeping you in the flow of the video.&lt;/p&gt;

&lt;p&gt;I like to visualize this process as a bucket brigade trying to keep a pool filled. The pool is your playback buffer, and the buckets are the video chunks.&lt;/p&gt;

&lt;p&gt;If my network bandwidth suddenly drops, the brigade can no longer carry the heavy 4K buckets fast enough to keep the pool full. If I keep trying to haul them, the pool runs dry, and the video freezes (classic buffering). To avoid this, the player quickly swaps out the heavy buckets for lighter, low-resolution ones. They do not look as pretty, but they arrive fast enough to prevent playback from stalling. Once my network recovers, the player can start fetching the heavy 4K buckets again.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does the client player decide which video chunk to download next?
&lt;/h2&gt;

&lt;p&gt;The client-side player is constantly measuring the exact download time of the preceding chunks to calculate active network bandwidth. If a chunk takes longer to arrive than its actual play duration, the player's internal logic immediately downgrades the resolution request for the next chunk.&lt;/p&gt;

&lt;p&gt;The video player is essentially running continuous telemetry on itself. When I hit play, the engine first fetches the manifest file. Because it does not have network telemetry yet, it usually plays it safe and starts by downloading a low- or mid-resolution chunk.&lt;/p&gt;

&lt;p&gt;If that first chunk downloads in a fraction of a second, the player realizes, &lt;em&gt;"I have plenty of bandwidth,"&lt;/em&gt; and immediately requests the ultra-high-res version for the second chunk. If a family member starts downloading a massive game update on the same Wi-Fi network, the player registers the delay on the third chunk and swiftly requests a lower-quality chunk for the fourth. It is a continuous loop of feedback and adjustment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is a manifest file in video streaming?
&lt;/h3&gt;

&lt;p&gt;When I talk about a manifest file (like HLS's &lt;code&gt;.m3u8&lt;/code&gt; or DASH's &lt;code&gt;.mpd&lt;/code&gt;), I mean a plain-text playlist that acts as a map. It indexes the URLs of all the individual video chunks, organized by resolution and bitrate, so the player knows exactly where to fetch them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why do videos often start in low quality before sharpening up?
&lt;/h3&gt;

&lt;p&gt;I notice this a lot on slower connections: players often default to a low-resolution chunk for the very first segment to minimize start-up latency. The goal is to start playing the video instantly, and once the player measures the actual download speed, it scales up to the highest quality.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the difference between HLS and DASH streaming?
&lt;/h3&gt;

&lt;p&gt;I view these as two sides of the same coin. Apple developed HLS (HTTP Live Streaming), which is native to iOS and Safari, whereas MPEG-DASH is an open international standard. Both accomplish adaptive streaming using the same chunk-swapping architecture but employ different file formats and manifest structures.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>videostreaming</category>
      <category>softwareengineering</category>
      <category>systemdesign</category>
    </item>
  </channel>
</rss>
