<?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: Aquil Abdullah</title>
    <description>The latest articles on DEV Community by Aquil Abdullah (@aabdullahbos).</description>
    <link>https://dev.to/aabdullahbos</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F158375%2F33c30d1c-8ac5-435a-8baf-a63d49e8deff.jpg</url>
      <title>DEV Community: Aquil Abdullah</title>
      <link>https://dev.to/aabdullahbos</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aabdullahbos"/>
    <language>en</language>
    <item>
      <title>AI Agents and the Things We Didn’t Know We Didn’t Know</title>
      <dc:creator>Aquil Abdullah</dc:creator>
      <pubDate>Wed, 06 May 2026 02:38:22 +0000</pubDate>
      <link>https://dev.to/aabdullahbos/ai-agents-and-the-things-we-didnt-know-we-didnt-know-1beb</link>
      <guid>https://dev.to/aabdullahbos/ai-agents-and-the-things-we-didnt-know-we-didnt-know-1beb</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally published at: &lt;a href="https://www.auklabs.io" rel="noopener noreferrer"&gt;https://www.auklabs.io&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;You've been building with an agentic tool for two weeks. Something breaks. You read the code and realize the agent made a decision you never asked it to make — and you have no idea when it made it or why. You make a change. Something else breaks. You make another change. Three changes later you're reading code you didn't write, trying to understand decisions you didn't make, and you've completely lost the thread of what you were originally trying to do. This is exactly what using an agent was supposed to help you avoid.&lt;/p&gt;

&lt;p&gt;That's not a bug. That's the gap.&lt;/p&gt;

&lt;p&gt;One of our biggest fears about agentic systems is that we won't know what they're doing. For me, the more interesting question is what leads to the decisions they're making — especially when tasks are underspecified or context is missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agents Are Precise
&lt;/h2&gt;

&lt;p&gt;Agentic coding tools are remarkably precise at executing what you ask them to do. Give an agent a goal and it can perform every task required to achieve that goal without asking for permission. That’s the feature, that’s the intent — agents assume that you’ve fully specified what you want, and where you haven’t, they make assumptions based on what they know. But that feature is also where things go quietly wrong.&lt;/p&gt;

&lt;p&gt;Agents don’t drift because they are careless; they drift because they are filling in gaps. Gaps in our specification, gaps in our instructions, gaps between what we said and what we meant. Agents fill in those gaps with inference and then move on. They don’t record what they inferred, and they don’t flag what was missing. They just execute. By the time we’ve realized that something has gone wrong, we’ve lost the thread of context that would help us answer &lt;em&gt;What&lt;/em&gt; and &lt;em&gt;Why&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The industry is starting to recognize this as a systemic issue. Engineers at Thoughtworks and elsewhere have started calling this "cognitive debt" — the erosion of shared understanding that accumulates when AI-generated code embeds design decisions invisibly in the output. It is the hidden cost of moving fast.&lt;/p&gt;

&lt;p&gt;This article is about a different approach: making gaps between intent and implementation visible before they compound, and making assumptions explicit while you’re still in a position to correct them.&lt;/p&gt;

&lt;h2&gt;
  
  
  An Unknown Unknown, Close Up
&lt;/h2&gt;

&lt;p&gt;Software development is, at its core, a process of discovery. When a human engineer works on a complex system, they carry deep, implicit knowledge about the system’s architecture, constraints, and unwritten rules. They know when to pause, when to ask a question, and when a decision is too consequential to make unilaterally.&lt;/p&gt;

&lt;p&gt;I’ve been frustrated by the gap between what I intend to build and what agentic tools actually produce — not because the tools are imprecise, but because I am. The assumptions that I make, the details that I leave out, and the instructions that I fail to give are the ones that cost me. What follows is a story about a gap I didn't know existed, surfaced by a methodology I’m building to find exactly these types of gaps.&lt;/p&gt;

&lt;p&gt;Despite using a combination of Test-Driven Design, Spec-Driven Design, and a prompt that attempts to minimally implement each unit of work (which I call a "Slice") in the pipeline, I received the following message at the end of one of those Slices:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;system.md&lt;/code&gt; referenced in the Slice 3 definition does not exist in the config. The configured artifacts cover the system-contract role. If &lt;code&gt;system.md&lt;/code&gt; is added as a formal artifact in a future slice, ArtifactRole and FOREIGN_RULES in &lt;code&gt;src/artifacts.ts&lt;/code&gt; must be extended.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A follow-up analysis revealed something worth paying attention to. The original configuration schema had been defined with five keys. Later, the file &lt;code&gt;system.md&lt;/code&gt; was added as a core artifact to provide guidance on what must remain true across all future work. The canonical execution prompt, also written separately, never mentioned it either. This meant that the reference document &lt;code&gt;system.md&lt;/code&gt; was defined, but not mentioned in the configuration file that described the core artifacts, the PRD, or the canonical execution prompt.&lt;/p&gt;

&lt;p&gt;Three documents. One gap. No record of a decision to exclude it.&lt;/p&gt;

&lt;p&gt;The absence wasn’t deliberate. There was just no recorded rationale for leaving it out. It was simply missing. The reference document clearly stated: &lt;em&gt;"The agent reads &lt;code&gt;system.md&lt;/code&gt; for what it must respect."&lt;/em&gt; The agent was executing correctly against an incomplete specification, and that was fine — until it wasn’t.&lt;/p&gt;

&lt;p&gt;I didn't know what I didn’t know. But the guardrails that I put in place for execution helped me identify the problem and reason about what went wrong. After observing that gap in execution, I ran a review, and the agent was able to explain why I didn’t know what I didn’t know. &lt;/p&gt;

&lt;p&gt;Here is the exact output the execution loop produced when it encountered the gap — not a crash, not a silent failure, but a structured explanation of what was missing and why it mattered:&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="gu"&gt;## Why system.md Is Not in raes.config.yaml&lt;/span&gt;

The config was designed in an execution slice (2026-04-19, Gap 1 in raes-reference.md Section 11). That slice defined 5 keys: &lt;span class="sb"&gt;`build_intent`&lt;/span&gt;, &lt;span class="sb"&gt;`next_slice`&lt;/span&gt;, &lt;span class="sb"&gt;`durable_decisions`&lt;/span&gt;, &lt;span class="sb"&gt;`execution_guidance`&lt;/span&gt;, &lt;span class="sb"&gt;`validation`&lt;/span&gt;.

&lt;span class="sb"&gt;`system.md`&lt;/span&gt; was &lt;span class="gs"&gt;**not included at that time**&lt;/span&gt;. The reference doc notes that &lt;span class="sb"&gt;`prd-ux-review.md`&lt;/span&gt; is &lt;span class="ge"&gt;*intentionally*&lt;/span&gt; absent from the config (Section 9, explicit). For &lt;span class="sb"&gt;`system.md`&lt;/span&gt;, there is no such explicit exclusion — its absence appears to be an oversight from when the schema was first defined, not a design decision.

Evidence supporting this interpretation:
&lt;span class="p"&gt;-&lt;/span&gt; raes-reference.md Section 3 describes system.md as a core truth artifact alongside PRD and decisions.md
&lt;span class="p"&gt;-&lt;/span&gt; raes-reference.md line 67: "The agent reads &lt;span class="sb"&gt;`system.md`&lt;/span&gt; for what it must respect."
&lt;span class="p"&gt;-&lt;/span&gt; Yet the canonical prompt (Section 7, lines 193-260) never mentions system.md, nor does it include a config key for it
&lt;span class="p"&gt;-&lt;/span&gt; The execution loop therefore has no directed path to the constraint surface system.md is meant to provide
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Stopping at the Right Moment
&lt;/h2&gt;

&lt;p&gt;The methodology that surfaced that gap is called RAES — Reusable AI Execution System. I want to be clear about what it is and what it isn't, because the name invites assumptions that aren't accurate.&lt;/p&gt;

&lt;p&gt;RAES is not an agent framework. It doesn't orchestrate tool calls or manage autonomous planning loops. It doesn't make the agent smarter or more capable. What it does is simpler and, I'd argue, more useful: it structures the moments where human judgment is required during execution, and it makes sure those moments leave a record.&lt;/p&gt;

&lt;p&gt;The core idea is that AI-assisted development has two distinct failure modes. The first is execution failure — the agent does something wrong. That's visible. Tests fail, code breaks, the problem surfaces quickly. The second is judgment failure — the agent does something reasonable that isn't what you intended, based on an assumption you didn't know it was making. That failure is invisible until it compounds.&lt;/p&gt;

&lt;p&gt;RAES is built for the second failure mode. It does this through three mechanisms:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. A structured execution loop&lt;/strong&gt; that separates two fundamentally different types of work — implementation and review — and applies different rules to each. Forcing planning work through an implementation loop produces bad outputs. Most tools don't make this distinction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. A decision record with a promotion rule.&lt;/strong&gt; When a decision is made during execution that will affect all future work, it gets recorded in two places: the rationale goes into &lt;code&gt;decisions.md&lt;/code&gt;, and the resulting constraint gets promoted into &lt;code&gt;system.md&lt;/code&gt; — the file whose absence started this whole story. It looks like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Decision (2026-04-19):&lt;/strong&gt; &lt;code&gt;system.md&lt;/code&gt; added as core truth artifact. Constraint promoted to &lt;code&gt;system.md&lt;/code&gt;: "All future slices must validate against the constraint surface defined here." Rationale: prevents silent exclusion of cross-cutting constraints from execution prompts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One artifact answers &lt;em&gt;why&lt;/em&gt;. The other answers &lt;em&gt;what must remain true&lt;/em&gt;. They reference each other. Future work reads the constraint without having to excavate the rationale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Explicit gap surfacing.&lt;/strong&gt; When the execution loop encounters something missing, conflicting, or ambiguous, it stops and flags it rather than inferring its way through. That's what happened with &lt;code&gt;system.md&lt;/code&gt;. The loop didn't guess. It named the gap, identified the downstream consequence, and handed it back to me.&lt;/p&gt;

&lt;p&gt;You might ask: &lt;em&gt;Why can't I just do this with a well-written &lt;code&gt;AGENTS.md&lt;/code&gt; or &lt;code&gt;.cursorrules&lt;/code&gt; file?&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;The difference is that those files are static. They describe how the agent should behave, but they do not record what the agent decided or why. They are rules, not a record. &lt;code&gt;decisions.md&lt;/code&gt; is the dynamic equivalent: it is updated &lt;em&gt;during&lt;/em&gt; execution, not before it. The distinction matters because the gap that caused the &lt;code&gt;system.md&lt;/code&gt; problem was not a missing rule — it was a missing record of a decision that was never made explicitly.&lt;/p&gt;

&lt;p&gt;That discipline is harder to build than it sounds. The default behavior of every agentic tool I've used is to keep moving. RAES is built around the assumption that stopping at the right moment is more valuable than executing at speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Don’t Know We Don’t Know – Yet
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;system.md&lt;/code&gt; gap wasn't the last one I found. It was just the first one I could explain clearly.&lt;/p&gt;

&lt;p&gt;That's the nature of unknown unknowns. You don't find them by being more careful or more thorough. You find them by building systems that are designed to look for them — and that say something when they do.&lt;/p&gt;

&lt;p&gt;RAES is early. The tooling is incomplete. &lt;code&gt;raes-execute&lt;/code&gt;, the CLI that will automate what I'm currently doing with a carefully constructed prompt, doesn't exist yet. The methodology is being used to build itself, which is either a sign that it works or a sign that I have an unusually high tolerance for recursive problems. Probably both.&lt;/p&gt;

&lt;p&gt;What I know is this: the discipline of making assumptions explicit, recording decisions as they happen, and surfacing gaps before they compound has changed how I work with agentic tools. Not because it slows things down — it doesn't, not meaningfully — but because it changes what I trust. I trust the output more when I can see the reasoning behind it. I trust the system more when I know it will stop and tell me what it doesn't know.&lt;/p&gt;

&lt;p&gt;That's not a feature any of the tools I compared against are optimizing for. They're optimizing for speed, for autonomy, for the feeling of progress. RAES optimizes for something quieter: the confidence that what got built is actually what you meant to build.&lt;/p&gt;

&lt;p&gt;If that gap resonates with you — if you've felt the specific frustration of knowing something went wrong but not knowing when the assumption that caused it was made — I'd like to hear about it. The repository is at &lt;a href="https://github.com/aabdullah-bos/raes" rel="noopener noreferrer"&gt;github.com/aabdullah-bos/raes&lt;/a&gt;. It's open, it's MIT licensed, and it's actively being developed. &lt;/p&gt;

&lt;p&gt;If you want to see the methodology in practice, start with &lt;code&gt;docs/raes-reference.md&lt;/code&gt; — it's the document the methodology used to build itself.&lt;/p&gt;

&lt;p&gt;If you're building something in this space, thinking about execution discipline for AI-assisted development, or just have a story about a gap you didn't know you didn't know — reach out. The methodology gets better when more people are finding the things they didn't know they were missing.&lt;/p&gt;

&lt;p&gt;The unknown unknowns are still out there. The point is to build systems that find them before they find you.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>aiworkflows</category>
      <category>agenticcodingassistants</category>
    </item>
    <item>
      <title>Vibe Coding Needs Telemetry</title>
      <dc:creator>Aquil Abdullah</dc:creator>
      <pubDate>Thu, 26 Mar 2026 20:00:19 +0000</pubDate>
      <link>https://dev.to/aabdullahbos/vibe-coding-needs-telemetry-29mi</link>
      <guid>https://dev.to/aabdullahbos/vibe-coding-needs-telemetry-29mi</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally published at: &lt;a href="https://www.aquilabdullah.com/your-post-url" rel="noopener noreferrer"&gt;https://www.aquilabdullah.com/your-post-url&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;I recently noticed something strange in the backend telemetry of a code base that I was working on.&lt;/p&gt;

&lt;p&gt;A single API request was triggering more than twenty database calls.&lt;/p&gt;

&lt;p&gt;The code looked perfectly reasonable, but the telemetry told a very different story.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Simple Vibe Coding Exercise
&lt;/h2&gt;

&lt;p&gt;Imagine you're building a simple profile endpoint.&lt;/p&gt;

&lt;p&gt;You ask your AI assistant to create something that returns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;user information
&lt;/li&gt;
&lt;li&gt;the sports they participate in
&lt;/li&gt;
&lt;li&gt;posts they've written
&lt;/li&gt;
&lt;li&gt;events they're attending
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A reasonable implementation might look 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="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_user_sports&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_user_posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_user_events&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sports&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;posts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;events&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, this looks great.&lt;/p&gt;

&lt;p&gt;Each function is small.&lt;br&gt;&lt;br&gt;
Each responsibility is clear.&lt;br&gt;&lt;br&gt;
The code is readable and easy to test.&lt;/p&gt;

&lt;p&gt;From the perspective of &lt;strong&gt;local code correctness&lt;/strong&gt;, this is good code.&lt;/p&gt;

&lt;p&gt;But from the perspective of &lt;strong&gt;system behavior&lt;/strong&gt;, something subtle may have just happened.&lt;/p&gt;


&lt;h2&gt;
  
  
  The N+1 Query Problem
&lt;/h2&gt;

&lt;p&gt;If each of those helper functions hits the database, this endpoint just turned into multiple queries.&lt;/p&gt;

&lt;p&gt;Instead of one database call, we now have several.&lt;/p&gt;

&lt;p&gt;This pattern is known as the &lt;strong&gt;N+1 query problem&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It usually appears when you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;run 1 query to fetch a list
&lt;/li&gt;
&lt;li&gt;then run N additional queries to fetch related data
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&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="nf"&gt;get_users&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;each&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;get_posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&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 load 10 users, that becomes 11 queries.&lt;br&gt;&lt;br&gt;
If you load 100 users, that becomes 101 queries.&lt;/p&gt;

&lt;p&gt;Each individual query is fast.&lt;/p&gt;

&lt;p&gt;But together they create unnecessary load and extra round trips.&lt;/p&gt;

&lt;p&gt;What started as clean, modular code quietly turns into a &lt;strong&gt;query fan-out pattern&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  When Telemetry Tells a Different Story
&lt;/h2&gt;

&lt;p&gt;It took me a minute to realize what I was looking at.&lt;/p&gt;

&lt;p&gt;The endpoint didn’t look suspicious, but the telemetry did.&lt;/p&gt;

&lt;p&gt;During a single request, I saw repeated database calls like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;21:15:40 GET /sports
21:15:40 GET /users
21:15:40 GET /event_rsvps
21:15:41 GET /sports
21:15:41 GET /users
21:15:41 GET /event_rsvps
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same resources being requested over and over again.&lt;/p&gt;

&lt;p&gt;The code looked clean.&lt;/p&gt;

&lt;p&gt;But the system was doing far more work than I expected.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Happens More With AI
&lt;/h2&gt;

&lt;p&gt;AI coding tools are very good at generating &lt;strong&gt;locally correct code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;They optimize for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;readability
&lt;/li&gt;
&lt;li&gt;modularity
&lt;/li&gt;
&lt;li&gt;clear abstractions
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But they don’t automatically reason about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;query fan-out
&lt;/li&gt;
&lt;li&gt;database round trips
&lt;/li&gt;
&lt;li&gt;system-level performance
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So you end up with code that looks right, but behaves differently than you expect at runtime.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fixing the Query Fan-Out
&lt;/h2&gt;

&lt;p&gt;Once you notice an N+1 pattern, the solution is usually to move more work into the database.&lt;/p&gt;

&lt;p&gt;Common approaches include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JOIN queries
&lt;/li&gt;
&lt;li&gt;database views
&lt;/li&gt;
&lt;li&gt;materialized views
&lt;/li&gt;
&lt;li&gt;RPC functions
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this case, I used a &lt;strong&gt;database RPC function&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of making multiple application-level calls, the database assembles the full result in a single operation.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before:
API → many database calls

After:
API → single RPC → database assembles result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reduces round trips and makes the endpoint behavior predictable.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Observability Mindset
&lt;/h2&gt;

&lt;p&gt;What struck me most about this bug was that the code itself looked perfectly reasonable.&lt;/p&gt;

&lt;p&gt;Nothing obviously inefficient.&lt;/p&gt;

&lt;p&gt;But telemetry told a different story.&lt;/p&gt;

&lt;p&gt;That’s the shift that comes with AI-assisted development.&lt;/p&gt;

&lt;p&gt;We can generate systems faster than ever.&lt;/p&gt;

&lt;p&gt;But speed makes it easier to miss how those systems behave under the hood.&lt;/p&gt;

&lt;p&gt;Telemetry gives you visibility into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how many queries an endpoint triggers
&lt;/li&gt;
&lt;li&gt;how requests flow through your system
&lt;/li&gt;
&lt;li&gt;where load is actually happening
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without it, you're relying on what the code suggests.&lt;/p&gt;

&lt;p&gt;With it, you can see what the system is actually doing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before and After
&lt;/h2&gt;

&lt;p&gt;Before the fix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request → ~20 database queries
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After moving the logic into an RPC function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request → 1 database call
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same endpoint.&lt;br&gt;&lt;br&gt;
Very different behavior.&lt;/p&gt;




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

&lt;p&gt;AI can generate endpoints quickly.&lt;br&gt;&lt;br&gt;
Telemetry tells you what those endpoints are actually doing.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>ai</category>
      <category>observability</category>
      <category>performance</category>
    </item>
    <item>
      <title>Co-programming with GPT-4o: A Love Story Gone Recursive</title>
      <dc:creator>Aquil Abdullah</dc:creator>
      <pubDate>Fri, 16 May 2025 23:33:36 +0000</pubDate>
      <link>https://dev.to/aabdullahbos/co-programming-with-gpt-4o-a-love-story-gone-recursive-3pf7</link>
      <guid>https://dev.to/aabdullahbos/co-programming-with-gpt-4o-a-love-story-gone-recursive-3pf7</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgch13zkgsg4a1ck0ledx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgch13zkgsg4a1ck0ledx.jpg" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before we get started, let’s get one thing straight: this article breaks no new ground!&lt;/p&gt;

&lt;p&gt;If you search Google, Bing, DuckDuckGo, or Perplexity for the phrase “Co-programming with GPT-4o,” you’ll find plenty of tales of woe about using GPT-4o as your pair programming partner.&lt;/p&gt;

&lt;p&gt;So why did I do it?&lt;/p&gt;

&lt;p&gt;Well, besides being a glutton for punishment, I asked GPT-4o,&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“What is the best OpenAI model for co-programming?”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And it replied:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“[GPT-4o] 🔥 Overall best for co-programming.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5a6g5zkpxmy27bw57ch7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5a6g5zkpxmy27bw57ch7.jpg" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So there you have it! ChatGPT-4o told me it was the &lt;strong&gt;&lt;em&gt;“Best of the best of the best…Sir!”&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  We Hit It Off Swell!
&lt;/h3&gt;

&lt;p&gt;Everything started out great. I had an idea, and GPT-4o chatted me up. It told me how amazing my idea was and promised to help turn it into an app.&lt;/p&gt;

&lt;p&gt;It provided me with a skeleton for the codebase and even gave me the exact statements I’d need to get started. We waxed philosophically about the merits of npm and yarn, and why it chose npm for this project.&lt;/p&gt;

&lt;p&gt;When I mentioned that I wanted to use TypeScript instead of JavaScript, GPT-4o kindly converted the code and pointed out which type definitions I needed to install.&lt;/p&gt;

&lt;p&gt;At this point, we were totally vibing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30751urk5q4bm1ayodpp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30751urk5q4bm1ayodpp.jpg" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was poo-pooing everyone who had claimed GPT-4o couldn’t code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;YOU DOWN WITH GPT?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;YEAH YOU KNOW ME.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Code? What Code?
&lt;/h3&gt;

&lt;p&gt;A few days later, I came back to my prototype to make some frontend changes. GPT-4o walked me through the edits, asked if I wanted it to generate a new file, and I said yes.&lt;/p&gt;

&lt;p&gt;I first suspected something was off when I noticed the new file was 50 lines shorter. When I ran the app, the styling was completely different.&lt;/p&gt;

&lt;p&gt;I asked what went wrong. GPT-4o told me it had lost context. I offered to upload the current version, and it obliged — we were back on track.&lt;/p&gt;

&lt;p&gt;There were a few more moments like this — context loss during refactoring, odd choices I wouldn’t have made — but I wrote them off as the cost of doing business.&lt;/p&gt;

&lt;p&gt;I was still down with GPT.&lt;/p&gt;

&lt;p&gt;But was GPT-4o still down with me?&lt;/p&gt;

&lt;h3&gt;
  
  
  The Ides of March or Et tu GPT-4o
&lt;/h3&gt;

&lt;p&gt;The final blow came when I was refactoring some logging logic. My good friend GPT-4o suggested we DRY up the code. I was like:&lt;/p&gt;

&lt;p&gt;“OK, GPT-4o, I see you modularizing my code and making reusable components!”&lt;/p&gt;

&lt;p&gt;It took this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LOG_PATH&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../logs&lt;/span&gt;&lt;span class="dl"&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;logPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;conversations.jsonl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And converted them into the function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getLogFilePath&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;logDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LOG_DIR&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;getLogFileDir&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// 🚨 infinite recursion!&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;logPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Path2D&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;conversations.jsonl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;logPath&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;Yep, you read that right: &lt;code&gt;Path2D.join(...)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I don’t know if that was hallucination or performance art, but either way, it wasn’t helpful. (And yes, I think it meant &lt;code&gt;path.join(...)&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;When I pointed out the infinite recursion, GPT-4o thanked me and recommended a fix.&lt;/p&gt;

&lt;p&gt;Still, I figured it was time to get to a stopping point and try a different model.&lt;/p&gt;

&lt;p&gt;So I asked GPT-4o to complete one last task: finish DRYing up a few more parts of the code.&lt;/p&gt;

&lt;p&gt;I uploaded the current file. Asked for a clean rewrite. GPT-4o gave me broken output.&lt;/p&gt;

&lt;p&gt;I tried again. And again. And again.&lt;/p&gt;

&lt;p&gt;Five uploads later, I finally figured out the issue: a newline character was being mishandled during patching.&lt;/p&gt;

&lt;p&gt;When I flagged it, GPT-4o jumped in and said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You’re spot on — the newline string is easy to break in patching logic because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;'\n' (Python-escaped newline) looks correct in code but often renders as 'n' or literal text if mishandled
&lt;/li&gt;
&lt;li&gt;This is a common gotcha in regex-based manipulation when we don’t parse the code structure semantically&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;I appreciated the honesty. But I also wanted working code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;There are things GPT-4o does really well. And to be fair, the model has already improved since this experiment. It was helpful with scaffolding, converting JavaScript to TypeScript, and even philosophical debates about dependency managers.&lt;/p&gt;

&lt;p&gt;But co-programming isn’t just about vibes. It’s about &lt;strong&gt;trust&lt;/strong&gt;, &lt;strong&gt;context&lt;/strong&gt;, and &lt;strong&gt;precision&lt;/strong&gt; — especially when you’re iterating on real-world code.&lt;/p&gt;

&lt;p&gt;If you decide to vibe-code with GPT-4o, make sure you’ve got version control, a good debugger, and a willingness to triple-check newline characters.&lt;/p&gt;

&lt;p&gt;As for whether GPT-4o is still “🔥 Overall best for co-programming”?&lt;/p&gt;

&lt;p&gt;Let’s just say… it's still under review.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>vibecoding</category>
      <category>debugging</category>
      <category>gpt4</category>
    </item>
  </channel>
</rss>
