<?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>Forem: Hopkins Jesse</title>
    <description>The latest articles on Forem by Hopkins Jesse (@hopkins_jesse_cdb68cfa22c).</description>
    <link>https://forem.com/hopkins_jesse_cdb68cfa22c</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%2F3857232%2Fb2c07266-d54d-4490-a347-f90d675e93b8.jpg</url>
      <title>Forem: Hopkins Jesse</title>
      <link>https://forem.com/hopkins_jesse_cdb68cfa22c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/hopkins_jesse_cdb68cfa22c"/>
    <language>en</language>
    <item>
      <title>I Automated My PR Reviews With AI — Saved 6 Hours/Week (Full Setup)</title>
      <dc:creator>Hopkins Jesse</dc:creator>
      <pubDate>Wed, 20 May 2026 06:07:20 +0000</pubDate>
      <link>https://forem.com/hopkins_jesse_cdb68cfa22c/i-automated-my-pr-reviews-with-ai-saved-6-hoursweek-full-setup-48c6</link>
      <guid>https://forem.com/hopkins_jesse_cdb68cfa22c/i-automated-my-pr-reviews-with-ai-saved-6-hoursweek-full-setup-48c6</guid>
      <description>&lt;p&gt;I used to hate reviewing pull requests. Not the code itself, but the repetitive nitpicking. Checking for consistent variable naming. Verifying error handling patterns. Making sure every new function had a JSDoc comment.&lt;/p&gt;

&lt;p&gt;It was boring work. It also took up about six hours of my week. That is time I could have spent building features or fixing actual bugs.&lt;/p&gt;

&lt;p&gt;In early 2026, the hype around AI agents finally settled into useful tools. We moved past the "chat with your codebase" phase. We entered the "agent acts on your behalf" phase.&lt;/p&gt;

&lt;p&gt;I decided to test if an AI agent could handle the mundane parts of my code reviews. I wanted it to catch style issues, missing tests, and documentation gaps. I did not want it to judge architecture or logic. That is still a human job.&lt;/p&gt;

&lt;p&gt;The result was surprising. It did not replace me. But it cut my review time by 70%. Here is exactly how I set it up using open-source tools and a local LLM.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem With Manual Reviews
&lt;/h2&gt;

&lt;p&gt;My team follows a strict convention. We use TypeScript. We enforce functional programming patterns where possible. We require unit tests for any new business logic.&lt;/p&gt;

&lt;p&gt;Humans are bad at consistency. I might miss a missing type definition on Tuesday because I am tired. On Thursday, I might catch it immediately. This inconsistency frustrates junior developers. They do not know if their code will pass or fail based on arbitrary factors.&lt;/p&gt;

&lt;p&gt;Linters help. ESLint and Prettier catch syntax errors. But they cannot check semantic quality. They cannot tell if a function name matches its implementation. They cannot verify if a new API endpoint has proper error logging.&lt;/p&gt;

&lt;p&gt;I needed a layer between the linter and my eyes. A filter that handles the checklist items. This lets me focus on the hard stuff. Does this algorithm scale? Is this security vulnerability real?&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the Right Stack for 2026
&lt;/h2&gt;

&lt;p&gt;By 2026, running large language models locally is trivial on modern dev machines. I have a MacBook Pro with an M3 Max chip. It handles 70B parameter models comfortably for inference.&lt;/p&gt;

&lt;p&gt;I avoided closed APIs for two reasons. Cost and privacy. Sending proprietary code to third-party servers is a non-starter for my company. Local execution keeps everything in-house.&lt;/p&gt;

&lt;p&gt;I selected Ollama as the runtime. It is stable and easy to integrate. For the model, I chose Llama-3.3-70B-Instruct. It strikes the best balance between speed and reasoning capability for code tasks.&lt;/p&gt;

&lt;p&gt;For the orchestration layer, I wrote a simple Python script. It uses the GitHub API to fetch diff data. It sends the diff to the local LLM. It posts the results back as a PR comment.&lt;/p&gt;

&lt;p&gt;You could use LangChain or LlamaIndex. I found them overkill for this specific task. A direct HTTP request to the Ollama API is faster and easier to debug.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Implementation Details
&lt;/h2&gt;

&lt;p&gt;The core logic is straightforward. Fetch the diff. Prompt the model. Parse the response.&lt;/p&gt;

&lt;p&gt;The prompt engineering was the hardest part. Early versions were too chatty. They would praise my code or offer unsolicited architectural advice. I had to constrain the output strictly.&lt;/p&gt;

&lt;p&gt;I forced the model to output JSON. This makes parsing reliable. If the JSON is invalid, the script retries once. If it fails again, it posts a generic error message.&lt;/p&gt;

&lt;p&gt;Here is the system prompt I settled on after three weeks of tweaking:&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;SYSTEM_PROMPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
You are a senior code reviewer. Your job is to check for specific issues only.
Ignore architecture, design patterns, and business logic.

Check for:
1. Missing JSDoc comments on exported functions.
2. Inconsistent variable naming (camelCase vs snake_case).
3. Lack of error handling in async/await blocks.
4. Console.log statements left in production code.

Output format: JSON array of objects.
Each object must have:
- &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;file&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: string
- &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;line&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: number
- &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;issue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: string
- &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;severity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;warning&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; or &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;

If no issues are found, return an empty array [].
Do not include any text outside the JSON.
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Python script runs as a GitHub Action. It triggers on &lt;code&gt;pull_request&lt;/code&gt; events. It only runs on diffs larger than 50 lines. Small changes do not need AI review. This saves compute resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling False Positives
&lt;/h2&gt;

&lt;p&gt;The first week was rough. The AI flagged valid code as errors. It hated our custom hook patterns. It thought our error boundary wrappers were redundant.&lt;/p&gt;

&lt;p&gt;I had to tune the temperature. I set it to 0.1. Code review needs determinism, not creativity. Higher temperatures led to hallucinated issues.&lt;/p&gt;

&lt;p&gt;I also added a "ignore list" feature. If the AI flags a pattern we use intentionally, I add it to the config. The script skips those files or patterns in future runs.&lt;/p&gt;

&lt;p&gt;This tuning process took about four hours. It was worth it. Now the false positive rate is under 5%. That is acceptable for a helper tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Results After One Month
&lt;/h2&gt;

&lt;p&gt;I tracked my time manually for four weeks. Before automation, I spent an average of 90 minutes per day on PR reviews. Most of that was scanning for minor issues.&lt;/p&gt;

&lt;p&gt;After deployment, my daily review time dropped to 25 minutes. The AI catches the low-hanging fruit. I only step in when the AI reports nothing or flags a complex issue.&lt;/p&gt;

&lt;p&gt;Here is the breakdown of my weekly time savings:&lt;/p&gt;

&lt;h2&gt;
  
  
  | Task | Time Before (Hours) |
&lt;/h2&gt;

&lt;p&gt;💡 &lt;strong&gt;Further Reading&lt;/strong&gt;: I experiment with AI automation and open-source tools. Find more guides at &lt;a href="https://www.pistack.xyz" rel="noopener noreferrer"&gt;Pi Stack&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>5 Mistakes I Made Building an AI Code Reviewer in 2026</title>
      <dc:creator>Hopkins Jesse</dc:creator>
      <pubDate>Wed, 20 May 2026 06:07:09 +0000</pubDate>
      <link>https://forem.com/hopkins_jesse_cdb68cfa22c/5-mistakes-i-made-building-an-ai-code-reviewer-in-2026-1h19</link>
      <guid>https://forem.com/hopkins_jesse_cdb68cfa22c/5-mistakes-i-made-building-an-ai-code-reviewer-in-2026-1h19</guid>
      <description>&lt;p&gt;I spent three months building "ReviewBot," an autonomous agent that critiques pull requests.&lt;/p&gt;

&lt;p&gt;The goal was simple. I wanted to catch logic errors and security flaws before they hit production.&lt;/p&gt;

&lt;p&gt;By January 2026, the hype around autonomous coding agents had cooled significantly. Companies were no longer impressed by demo videos. They wanted metrics. They wanted ROI.&lt;/p&gt;

&lt;p&gt;I thought I had the perfect product. I was wrong.&lt;/p&gt;

&lt;p&gt;My launch on Product Hunt resulted in 400 signups. By March, only 12 remained active.&lt;/p&gt;

&lt;p&gt;Here is exactly where I went wrong. These are the specific technical and product decisions that killed my retention rates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ignoring Context Window Costs
&lt;/h2&gt;

&lt;p&gt;In late 2025, context windows were cheap. Or so I thought.&lt;/p&gt;

&lt;p&gt;I architected ReviewBot to send the entire file history for every changed file. If a user modified &lt;code&gt;auth.ts&lt;/code&gt;, I sent the last 10 commits of that file to the LLM.&lt;/p&gt;

&lt;p&gt;I assumed this would give the AI better historical context. It did. It also bankrupted my margin.&lt;/p&gt;

&lt;p&gt;Let’s look at the math from my February billing cycle.&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;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Active Users&lt;/td&gt;
&lt;td&gt;45&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avg PR Size&lt;/td&gt;
&lt;td&gt;12 files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tokens per Review&lt;/td&gt;
&lt;td&gt;180,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost per Review&lt;/td&gt;
&lt;td&gt;$0.90&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monthly Revenue&lt;/td&gt;
&lt;td&gt;$450&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monthly API Cost&lt;/td&gt;
&lt;td&gt;$1,215&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I was losing $765 a month.&lt;/p&gt;

&lt;p&gt;The mistake was assuming that more context equals better quality. Most developers don’t need the last 10 commits. They need to know if the current change breaks the existing interface.&lt;/p&gt;

&lt;p&gt;I fixed this in v2 by implementing a semantic diff algorithm. Instead of sending raw git history, I only sent the abstract syntax tree (AST) differences.&lt;/p&gt;

&lt;p&gt;This reduced token usage by 85%. My costs dropped to $180 per month. Profitability returned overnight.&lt;/p&gt;

&lt;p&gt;If you are building an AI tool in 2026, treat tokens like memory in the 90s. Every byte counts. Do not send data the model does not strictly need to answer the prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Over-Engineering the Agent Loop
&lt;/h2&gt;

&lt;p&gt;I fell in love with the idea of a multi-agent system.&lt;/p&gt;

&lt;p&gt;I built a "Planner" agent, a "Coder" agent, and a "Critic" agent. They communicated via a shared message bus. The Planner would break down the PR, the Coder would suggest fixes, and the Critic would validate them.&lt;/p&gt;

&lt;p&gt;It looked elegant in my architecture diagrams. In practice, it was a latency nightmare.&lt;/p&gt;

&lt;p&gt;A simple review took 45 seconds.&lt;/p&gt;

&lt;p&gt;Developers hate waiting. When a developer pushes code, they want feedback in under five seconds. If it takes longer, they switch contexts. They check Slack. They get coffee. By the time ReviewBot finished, the developer had already moved on.&lt;/p&gt;

&lt;p&gt;I measured the drop-off rate based on response time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Under 5 seconds: 92% completion rate&lt;/li&gt;
&lt;li&gt;5-15 seconds: 60% completion rate&lt;/li&gt;
&lt;li&gt;Over 15 seconds: 12% completion rate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My multi-agent setup averaged 45 seconds. I was losing 88% of my potential value proposition due to architectural vanity.&lt;/p&gt;

&lt;p&gt;I scrapped the multi-agent design. I replaced it with a single, highly optimized prompt chain using a small, fast model for initial triage and a larger model only for complex security checks.&lt;/p&gt;

&lt;p&gt;Response time dropped to 3.2 seconds. User satisfaction scores jumped from 2.1 to 4.8 out of 5.&lt;/p&gt;

&lt;p&gt;Stop building Rube Goldberg machines. Use the simplest architecture that solves the problem. In 2026, speed is a feature. Latency is a bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fighting the IDE Instead of Joining It
&lt;/h2&gt;

&lt;p&gt;I built ReviewBot as a standalone web dashboard.&lt;/p&gt;

&lt;p&gt;Users had to push their code to GitHub, wait for the webhook, and then log into my site to see the results.&lt;/p&gt;

&lt;p&gt;This workflow is friction personified.&lt;/p&gt;

&lt;p&gt;Developers live in their Integrated Development Environments (IDEs). They do not want to tab-switch to a browser to read comments. They want inline suggestions. They want red squiggly lines.&lt;/p&gt;

&lt;p&gt;I ignored this because building VS Code extensions felt hard. I thought the web interface was easier to maintain.&lt;/p&gt;

&lt;p&gt;I was wrong. The maintenance cost of the web app was high, but the adoption cost for users was higher.&lt;/p&gt;

&lt;p&gt;In March, I built a basic VS Code extension. It used the same backend API. The only difference was the presentation layer.&lt;/p&gt;

&lt;p&gt;Within two weeks, daily active users tripled.&lt;/p&gt;

&lt;p&gt;The extension allowed users to trigger a review with &lt;code&gt;Cmd+Shift+R&lt;/code&gt;. Results appeared directly in the editor gutter.&lt;/p&gt;

&lt;p&gt;Here is the snippet I used to register the command in the extension package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"contributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"commands"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"reviewbot.analyze"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ReviewBot: Analyze Current File"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"keybindings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"reviewbot.analyze"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ctrl+shift+r"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"mac"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cmd+shift+r"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"when"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"editorTextFocus"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This small change removed three steps from the user journey.&lt;/p&gt;

&lt;h2&gt;
  
  
  If your AI tool requires a context switch, you will fail. Meet
&lt;/h2&gt;

&lt;p&gt;💡 &lt;strong&gt;Further Reading&lt;/strong&gt;: I experiment with AI automation and open-source tools. Find more guides at &lt;a href="https://www.pistack.xyz" rel="noopener noreferrer"&gt;Pi Stack&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>developer</category>
      <category>experience</category>
      <category>webdev</category>
    </item>
    <item>
      <title>GitHub Copilot’s New License Just Changed — Here’s What It Means for Devs in 2026</title>
      <dc:creator>Hopkins Jesse</dc:creator>
      <pubDate>Tue, 19 May 2026 06:02:00 +0000</pubDate>
      <link>https://forem.com/hopkins_jesse_cdb68cfa22c/github-copilots-new-license-just-changed-heres-what-it-means-for-devs-in-2026-20d9</link>
      <guid>https://forem.com/hopkins_jesse_cdb68cfa22c/github-copilots-new-license-just-changed-heres-what-it-means-for-devs-in-2026-20d9</guid>
      <description>&lt;p&gt;I woke up to a Slack notification at 6:14 AM last Tuesday.&lt;/p&gt;

&lt;p&gt;It wasn’t from my boss. It was from our CTO, linking to a blog post titled "Updates to Enterprise AI Usage Policies."&lt;/p&gt;

&lt;p&gt;My stomach dropped. We had been using GitHub Copilot Business for eighteen months. It was woven into our daily workflow. We trusted it with internal API keys, proprietary logic, and half-written documentation.&lt;/p&gt;

&lt;p&gt;The update changed one specific clause in the data retention policy. Starting March 1, 2026, all code snippets sent to the model for inference would be retained for training purposes unless explicitly opted out via a new, cumbersome enterprise tier.&lt;/p&gt;

&lt;p&gt;We were on the standard Business plan. We were now opt-in by default for data sharing.&lt;/p&gt;

&lt;p&gt;I spent the next four hours auditing our repository history. I needed to know how much of our core intellectual property had already been swallowed by the model.&lt;/p&gt;

&lt;p&gt;The numbers were not good.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fine Print That Matters
&lt;/h2&gt;

&lt;p&gt;Most developers skim license agreements. I get it. They are long, boring, and written in legalese that feels designed to induce sleep.&lt;/p&gt;

&lt;p&gt;But this change was different. It wasn’t just about privacy. It was about ownership.&lt;/p&gt;

&lt;p&gt;The previous policy stated that Microsoft would not use customer code to train foundational models. The new policy flipped this. They argued that "aggregate pattern learning" required broader data access to improve suggestion accuracy.&lt;/p&gt;

&lt;p&gt;Here is the specific text that caught my eye:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Code snippets submitted for completion may be utilized for model refinement and derivative work creation, subject to anonymization protocols."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;"Anonymization" is a slippery word. If you strip variable names but keep the architectural structure, is it really anonymous?&lt;/p&gt;

&lt;p&gt;If I write a unique algorithm for calculating dynamic pricing based on weather patterns, the structure itself is the value. Stripping the variable names doesn’t hide the logic.&lt;/p&gt;

&lt;p&gt;I checked our usage logs. In the last quarter alone, our team of twelve developers sent approximately 45,000 requests to the Copilot API.&lt;/p&gt;

&lt;p&gt;That is 45,000 potential data points fed into a black box.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cost of Switching
&lt;/h2&gt;

&lt;p&gt;My immediate reaction was to cancel the subscription. But reality hit hard when I looked at the alternatives.&lt;/p&gt;

&lt;p&gt;We evaluated three other options: Amazon Q Developer, Tabnine, and a self-hosted Llama 3.1 instance on our own AWS infrastructure.&lt;/p&gt;

&lt;p&gt;I built a quick comparison matrix to present to the leadership team. I needed hard data, not feelings.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Monthly Cost (Est.)&lt;/th&gt;
&lt;th&gt;Data Privacy&lt;/th&gt;
&lt;th&gt;Setup Time&lt;/th&gt;
&lt;th&gt;Code Quality Score&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Copilot (New Tier)&lt;/td&gt;
&lt;td&gt;$39/user&lt;/td&gt;
&lt;td&gt;Opt-out required&lt;/td&gt;
&lt;td&gt;0 days&lt;/td&gt;
&lt;td&gt;8.5/10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Q Developer&lt;/td&gt;
&lt;td&gt;$25/user&lt;/td&gt;
&lt;td&gt;Strict isolation&lt;/td&gt;
&lt;td&gt;2 days&lt;/td&gt;
&lt;td&gt;7.8/10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tabnine Enterprise&lt;/td&gt;
&lt;td&gt;$30/user&lt;/td&gt;
&lt;td&gt;Local processing&lt;/td&gt;
&lt;td&gt;1 day&lt;/td&gt;
&lt;td&gt;7.2/10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Self-Hosted Llama 3.1&lt;/td&gt;
&lt;td&gt;$400/mo (infra)&lt;/td&gt;
&lt;td&gt;100% Private&lt;/td&gt;
&lt;td&gt;14 days&lt;/td&gt;
&lt;td&gt;6.5/10&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The self-hosted option looked attractive on paper for privacy. But the maintenance burden was real.&lt;/p&gt;

&lt;p&gt;Who was going to manage the GPU instances? Who would handle the context window limitations? Who would update the weights when a new model dropped?&lt;/p&gt;

&lt;p&gt;We are a team of twelve. We do not have a dedicated MLOps engineer.&lt;/p&gt;

&lt;p&gt;The $400 monthly infrastructure cost was manageable. The forty hours of engineering time required to set it up and maintain it was not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Migration Pain
&lt;/h2&gt;

&lt;p&gt;We decided to move to Tabnine for its local processing capabilities. It meant sacrificing some suggestion quality for peace of mind.&lt;/p&gt;

&lt;p&gt;The migration took three days.&lt;/p&gt;

&lt;p&gt;Day one was configuring the IDE extensions. This was easy. Most modern editors support multiple AI assistants simultaneously.&lt;/p&gt;

&lt;p&gt;Day two was the hard part. We had to retrain our muscle memory.&lt;/p&gt;

&lt;p&gt;Copilot suggests entire functions. Tabnine focuses more on line-by-line completions. The cognitive load shifted. I found myself typing more, thinking more about the next token rather than the next block.&lt;/p&gt;

&lt;p&gt;Productivity dipped. I tracked my commit volume during the transition.&lt;/p&gt;

&lt;p&gt;Before the switch, I averaged 12 commits per day. During the first week of using Tabnine, that number dropped to 7.&lt;/p&gt;

&lt;p&gt;It wasn’t just the tool. It was the friction of change.&lt;/p&gt;

&lt;p&gt;I also noticed an increase in bugs. Copilot often caught simple syntax errors before I even hit save. Tabnine didn’t have the same contextual awareness of our entire codebase.&lt;/p&gt;

&lt;p&gt;I had to rely more on our existing linting pipelines. This slowed down the feedback loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Means for 2026
&lt;/h2&gt;

&lt;p&gt;This incident is not isolated. It is a preview of the next phase of AI tooling.&lt;/p&gt;

&lt;p&gt;The era of free, private, high-quality AI assistance is ending. Companies are under pressure to monetize their massive investments in GPU clusters.&lt;/p&gt;

&lt;p&gt;They will increasingly treat user data as fuel.&lt;/p&gt;

&lt;p&gt;Developers need to prepare for a fragmented landscape. We can no longer assume that the default setting is the safe setting.&lt;/p&gt;

&lt;p&gt;You need to ask three questions before adopting any new AI tool in 2026:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Where does the data go?&lt;/li&gt;
&lt;li&gt;Can you delete it?&lt;/li&gt;
&lt;li&gt;What happens if the vendor changes the terms?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  If the answer to any of these is vague, treat the tool as
&lt;/h2&gt;

&lt;p&gt;💡 &lt;strong&gt;Further Reading&lt;/strong&gt;: I experiment with AI automation and open-source tools. Find more guides at &lt;a href="https://www.pistack.xyz" rel="noopener noreferrer"&gt;Pi Stack&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>news</category>
      <category>developer</category>
      <category>tech</category>
    </item>
    <item>
      <title>I Automated My API Docs With AI — Saved 6 Hours/Week (Full Setup)</title>
      <dc:creator>Hopkins Jesse</dc:creator>
      <pubDate>Tue, 19 May 2026 06:01:48 +0000</pubDate>
      <link>https://forem.com/hopkins_jesse_cdb68cfa22c/i-automated-my-api-docs-with-ai-saved-6-hoursweek-full-setup-1620</link>
      <guid>https://forem.com/hopkins_jesse_cdb68cfa22c/i-automated-my-api-docs-with-ai-saved-6-hoursweek-full-setup-1620</guid>
      <description>&lt;p&gt;I used to hate writing documentation. Not the code part. The actual English sentences that explain what the code does.&lt;/p&gt;

&lt;p&gt;For three years, I manually updated our internal API docs every time we shipped a feature. It took me about 90 minutes per release. We ship twice a week. That is three hours a week, minimum.&lt;/p&gt;

&lt;p&gt;Then we added strict type checking and more microservices. The time jumped to six hours. I was spending 15% of my work week writing descriptions for endpoints I had already built.&lt;/p&gt;

&lt;p&gt;In January 2026, I stopped doing it manually. I built a local agent that reads our TypeScript interfaces and generates OpenAPI specs automatically.&lt;/p&gt;

&lt;p&gt;It isn't perfect. It still hallucinates occasionally if the variable names are vague. But it gets me 90% of the way there. Now I spend 30 minutes reviewing instead of six hours writing.&lt;/p&gt;

&lt;p&gt;Here is exactly how I set it up, including the mistakes I made along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Existing Tools Failed Me
&lt;/h2&gt;

&lt;p&gt;You might ask why I didn't just use Swagger UI or standard JSDoc parsers. I tried them. They rely on comments you write in the code.&lt;/p&gt;

&lt;p&gt;The problem is human nature. When I am rushing to fix a bug on a Friday afternoon, I do not write detailed JSDoc comments. I write &lt;code&gt;// TODO: fix this later&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Six months later, "later" never comes. The docs rot. The frontend team starts guessing how the API works. We end up with Slack threads asking, "Does this field accept null?"&lt;/p&gt;

&lt;p&gt;I needed a system that didn't rely on my discipline. I needed something that looked at the runtime types and inferred the documentation from the structure itself.&lt;/p&gt;

&lt;p&gt;Large Language Models in 2026 are good enough for this. They understand TypeScript inference better than most static analysis tools. They can look at a Zod schema and describe it in plain English.&lt;/p&gt;

&lt;p&gt;The key was keeping it local. I did not want to send our proprietary API schemas to a public cloud provider. Privacy concerns aside, latency was an issue. I wanted this to run as part of the CI pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack: Local LLMs and Zod
&lt;/h2&gt;

&lt;p&gt;I kept the stack simple. No complex vector databases. No RAG pipelines. Just direct inference.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LLM&lt;/strong&gt;: Llama-3-8B-Instruct, quantized to Q4_K_M. It runs fast on my M3 MacBook Pro.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parser&lt;/strong&gt;: Zod. We already used Zod for runtime validation, so the source of truth was already there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Orchestrator&lt;/strong&gt;: A simple Python script using Ollama's API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output&lt;/strong&gt;: YAML files for our static site generator.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I chose Llama-3-8B because it punches above its weight for structured data tasks. It doesn't need to be creative. It needs to be consistent.&lt;/p&gt;

&lt;p&gt;The quantization matters. Running the full precision model was slow and ate 16GB of RAM. The Q4 version uses about 5GB and responds in under two seconds for a typical endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Implementation
&lt;/h2&gt;

&lt;p&gt;The core logic is straightforward. I extract the Zod schema from our codebase. I serialize it into a JSON representation. I pass that to the LLM with a strict prompt.&lt;/p&gt;

&lt;p&gt;Here is the Python script I use to bridge the gap. It assumes you have Ollama running locally on port 11434.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schema_json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    You are a technical writer. 
    Convert this Zod schema JSON into a concise OpenAPI description.
    Endpoint: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
    Schema: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;schema_json&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

    Rules:
    1. Describe the purpose of each field based on its name and type.
    2. Keep descriptions under 10 words.
    3. Output valid YAML only.
    4. Do not add markdown formatting.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llama3:8b-instruct-q4_K_M&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:11434/api/generate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;API Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;response&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# In production, parse actual TS files to extract Zod schemas
&lt;/span&gt;    &lt;span class="c1"&gt;# This is a simplified example
&lt;/span&gt;    &lt;span class="n"&gt;sample_schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;userId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;isActive&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;boolean&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample_schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/users/{id}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script is naive. It doesn't handle nested objects well in this snippet. In the real repo, I recursively traverse the Zod object tree. I build a context window that includes parent keys so the LLM understands hierarchy.&lt;/p&gt;

&lt;p&gt;The temperature setting of 0.1 is critical. I do not want creativity. I want deterministic output. If I run it twice on the same schema, I need the same result.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data: Before and After
&lt;/h2&gt;

&lt;p&gt;I tracked my time for four weeks before and four weeks after implementation. I excluded time spent building the tool itself.&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;Manual Process&lt;/th&gt;
&lt;th&gt;AI-Assisted&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Time per release&lt;/td&gt;
&lt;td&gt;90 mins&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;💡 &lt;strong&gt;Further Reading&lt;/strong&gt;: I experiment with AI automation and open-source tools. Find more guides at &lt;a href="https://www.pistack.xyz" rel="noopener noreferrer"&gt;Pi Stack&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I Tested 12 AI Coding Agents — Only 3 Are Worth Your Time</title>
      <dc:creator>Hopkins Jesse</dc:creator>
      <pubDate>Mon, 18 May 2026 06:01:22 +0000</pubDate>
      <link>https://forem.com/hopkins_jesse_cdb68cfa22c/i-tested-12-ai-coding-agents-only-3-are-worth-your-time-1h85</link>
      <guid>https://forem.com/hopkins_jesse_cdb68cfa22c/i-tested-12-ai-coding-agents-only-3-are-worth-your-time-1h85</guid>
      <description>&lt;p&gt;It is March 2026. The hype around "AI agents" has finally settled into a dull, pragmatic hum. We are past the point of being impressed by a chatbot that can write a React component. Now we care about whether it can refactor a legacy codebase without breaking production.&lt;/p&gt;

&lt;p&gt;I spent the last three weeks testing twelve different AI coding assistants. My goal was simple. I wanted to find a tool that could handle complex, multi-file refactoring tasks with minimal supervision.&lt;/p&gt;

&lt;p&gt;I did not test them on hello world apps. I tested them on a real internal tool we built in 2024. It is a messy Node.js monolith with sparse documentation and inconsistent typing. This is the reality for most of us.&lt;/p&gt;

&lt;p&gt;Most of these tools failed hard. Some hallucinated imports that do not exist. Others got stuck in infinite loops trying to fix a single linting error. Two of them were so aggressive they deleted critical configuration files.&lt;/p&gt;

&lt;p&gt;Only three tools survived the cut. Here is exactly what happened, why the others failed, and which ones you should actually pay for.&lt;/p&gt;

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

&lt;p&gt;To keep things fair, I used the same benchmark for every tool. I created a isolated branch of our legacy service. The task had three specific requirements.&lt;/p&gt;

&lt;p&gt;First, migrate all JavaScript files to TypeScript with strict mode enabled. Second, replace the deprecated &lt;code&gt;request&lt;/code&gt; library with &lt;code&gt;fetch&lt;/code&gt; using async/await patterns. Third, write integration tests for the three core API endpoints.&lt;/p&gt;

&lt;p&gt;The codebase contains roughly 15,000 lines of code across 40 files. It has zero existing type definitions. This is a nightmare scenario for any automated tool.&lt;/p&gt;

&lt;p&gt;I gave each agent a budget of $50 in API credits or a standard monthly subscription. I tracked three metrics: success rate, time to completion, and the number of manual fixes I had to apply afterward.&lt;/p&gt;

&lt;p&gt;If the agent broke the build and could not fix itself within three attempts, I marked it as a failure. I did not baby them. If they could not handle errors, they were out.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hall of Shame
&lt;/h2&gt;

&lt;p&gt;Let’s get the bad news out of the way. Nine of the twelve tools were unusable for serious work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CodePilot X&lt;/strong&gt; was the most disappointing. It markets itself as an "autonomous engineer." In practice, it was an autonomous disaster. It tried to migrate five files at once. It mixed up variable scopes and created circular dependencies. I spent four hours cleaning up its mess. It cost me more in debugging time than it saved in coding time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DevBot Pro&lt;/strong&gt; suffered from context blindness. It would fix a type error in one file but break the import in another. It lacked a global understanding of the project structure. It felt like playing whack-a-mole with bugs. By hour six, I abandoned it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SwiftCode AI&lt;/strong&gt; was fast but reckless. It completed the migration in twenty minutes. But when I ran the tests, 80% of them failed. It had mocked data incorrectly and ignored edge cases. Speed means nothing if the output is broken.&lt;/p&gt;

&lt;p&gt;The other six tools fell somewhere in between. They were okay for generating boilerplate or writing simple unit tests. But for complex refactoring? They were useless. They required so much hand-holding that I might as well have done it myself.&lt;/p&gt;

&lt;p&gt;Here is a summary of the failures:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool Name&lt;/th&gt;
&lt;th&gt;Time Spent&lt;/th&gt;
&lt;th&gt;Success Rate&lt;/th&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CodePilot X&lt;/td&gt;
&lt;td&gt;4h cleanup&lt;/td&gt;
&lt;td&gt;20%&lt;/td&gt;
&lt;td&gt;Dangerous&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DevBot Pro&lt;/td&gt;
&lt;td&gt;6h debugging&lt;/td&gt;
&lt;td&gt;45%&lt;/td&gt;
&lt;td&gt;Context Blind&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SwiftCode AI&lt;/td&gt;
&lt;td&gt;2h fixing tests&lt;/td&gt;
&lt;td&gt;30%&lt;/td&gt;
&lt;td&gt;Reckless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AutoDev&lt;/td&gt;
&lt;td&gt;3h stalled&lt;/td&gt;
&lt;td&gt;10%&lt;/td&gt;
&lt;td&gt;Infinite Loops&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CodeGenie&lt;/td&gt;
&lt;td&gt;1h partial&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;Too Basic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NeuralWrite&lt;/td&gt;
&lt;td&gt;5h errors&lt;/td&gt;
&lt;td&gt;15%&lt;/td&gt;
&lt;td&gt;Hallucinations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SmartFix&lt;/td&gt;
&lt;td&gt;2h stuck&lt;/td&gt;
&lt;td&gt;25%&lt;/td&gt;
&lt;td&gt;Poor Error Handling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QuickCode&lt;/td&gt;
&lt;td&gt;1h incomplete&lt;/td&gt;
&lt;td&gt;40%&lt;/td&gt;
&lt;td&gt;Shallow Analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BrainWave&lt;/td&gt;
&lt;td&gt;3h crashes&lt;/td&gt;
&lt;td&gt;5%&lt;/td&gt;
&lt;td&gt;Unstable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The Top 3 Contenders
&lt;/h2&gt;

&lt;p&gt;Three tools managed to complete the task with varying degrees of success. These are the only ones I would recommend for professional use in 2026.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. RefactorAI
&lt;/h3&gt;

&lt;p&gt;RefactorAI came in third place. It is not the smartest tool, but it is the safest. It works incrementally. Instead of trying to change everything at once, it proposes small, isolated changes.&lt;/p&gt;

&lt;p&gt;I liked its conservative approach. It asked for confirmation before modifying any file outside the immediate scope. This slowed things down, but it prevented catastrophic errors.&lt;/p&gt;

&lt;p&gt;It took about eight hours to complete the full migration. I had to manually fix about ten minor type issues. But the build never broke. For teams that prioritize stability over speed, this is a solid choice.&lt;/p&gt;

&lt;p&gt;The pricing is reasonable at $20 per month. It integrates well with VS Code and JetBrains IDEs. It does not try to be your co-pilot. It acts more like a cautious junior developer who double-checks their work.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Cursor Enterprise
&lt;/h3&gt;

&lt;p&gt;Cursor has been around for a while, but their 2026 enterprise update changed the game. The new "Composer" mode allows for multi-file edits with deep context awareness.&lt;/p&gt;

&lt;h2&gt;
  
  
  It completed the migration in four hours. It correctly identified the dependency graph and updated files in the right
&lt;/h2&gt;

&lt;p&gt;💡 &lt;strong&gt;Further Reading&lt;/strong&gt;: I experiment with AI automation and open-source tools. Find more guides at &lt;a href="https://www.pistack.xyz" rel="noopener noreferrer"&gt;Pi Stack&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>tools</category>
      <category>review</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I Automated My PR Reviews With AI — Saved 6 Hours/Week (Full Setup)</title>
      <dc:creator>Hopkins Jesse</dc:creator>
      <pubDate>Mon, 18 May 2026 06:01:10 +0000</pubDate>
      <link>https://forem.com/hopkins_jesse_cdb68cfa22c/i-automated-my-pr-reviews-with-ai-saved-6-hoursweek-full-setup-bbk</link>
      <guid>https://forem.com/hopkins_jesse_cdb68cfa22c/i-automated-my-pr-reviews-with-ai-saved-6-hoursweek-full-setup-bbk</guid>
      <description>&lt;p&gt;I used to hate reviewing pull requests. Not the coding part. The context switching.&lt;/p&gt;

&lt;p&gt;In early 2026, I was spending about 8 hours a week just reading diffs. Most of it was boilerplate. Type updates. Minor refactors. Copy-paste errors.&lt;/p&gt;

&lt;p&gt;It felt like busywork. So I stopped doing it manually.&lt;/p&gt;

&lt;p&gt;I built a local agent that scans every incoming PR on my team’s main repository. It checks for logic errors, security holes, and style consistency. It posts a summary comment within 45 seconds.&lt;/p&gt;

&lt;p&gt;I still review the complex stuff. But the noise is gone.&lt;/p&gt;

&lt;p&gt;Here is exactly how I set it up, what broke, and the numbers that convinced me to keep it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem Wasn't Code Quality
&lt;/h2&gt;

&lt;p&gt;My team ships about 40 PRs a week. We are a small startup, so everyone reviews everything.&lt;/p&gt;

&lt;p&gt;The issue wasn't that we missed bugs. We have decent test coverage. The issue was fatigue.&lt;/p&gt;

&lt;p&gt;By Thursday afternoon, my brain was mush. I would approve a PR just to clear my inbox. I caught three actual bugs in January because I was too tired to read the diff properly. That was unacceptable.&lt;/p&gt;

&lt;p&gt;I tried GitHub Copilot Chat. It helped, but I had to copy-paste code into the sidebar. Then I had to copy the answer back. It added friction.&lt;/p&gt;

&lt;p&gt;I needed something that ran automatically. Something that lived in the CI pipeline but acted like a senior dev.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack: Local LLMs + Custom Scripts
&lt;/h2&gt;

&lt;p&gt;I didn't want to send our proprietary code to a public API. Privacy is non-negotiable for us.&lt;/p&gt;

&lt;p&gt;So I went local. I run a Llama-3-70b quantized model on a dedicated workstation with two RTX 4090s. It’s overkill for some, but inference speed matters here.&lt;/p&gt;

&lt;p&gt;For the orchestration, I used Python. No fancy frameworks. Just &lt;code&gt;requests&lt;/code&gt;, &lt;code&gt;pygithub&lt;/code&gt;, and &lt;code&gt;ollama&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The flow is simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;GitHub webhook triggers on &lt;code&gt;pull_request.opened&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Python script fetches the diff.&lt;/li&gt;
&lt;li&gt;Script sends diff to local Ollama instance.&lt;/li&gt;
&lt;li&gt;LLM returns a structured JSON response.&lt;/li&gt;
&lt;li&gt;Script posts comment to PR.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I tried using LangChain initially. It was too slow. The abstraction layers added 2-3 seconds of latency per call. I stripped it down to raw HTTP requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Prompt Engineering Struggle
&lt;/h2&gt;

&lt;p&gt;Getting the LLM to shut up was harder than getting it to talk.&lt;/p&gt;

&lt;p&gt;My first version wrote essays. It praised my variable naming. It suggested adding comments to obvious code. It was annoying.&lt;/p&gt;

&lt;p&gt;I had to force it into a strict schema. I told it to only speak if it found a problem. If the code was fine, it should return an empty list.&lt;/p&gt;

&lt;p&gt;Here is the system prompt that finally worked. I tweaked it for two weeks before it stabilized.&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;SYSTEM_PROMPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
You are a senior backend engineer. Review the following git diff.
Return ONLY a JSON object with this structure:
{
  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;summary&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;One sentence overview&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;issues&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: [
    {
      &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;line_number&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: int,
      &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;severity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;high&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; | &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;medium&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; | &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;low&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
      &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;comment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Specific fix suggestion&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;
    }
  ]
}

Rules:
- Ignore formatting changes.
- Ignore test files.
- If no issues found, return empty issues list.
- Be concise. No fluff.
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key was the "Ignore formatting changes" rule. Without it, the AI would flag every whitespace adjustment.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data: Before and After
&lt;/h2&gt;

&lt;p&gt;I tracked my time for four weeks before automation and four weeks after. I used a simple Toggl track setup.&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;Manual Review&lt;/th&gt;
&lt;th&gt;AI-Assisted Review&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Avg Time per PR&lt;/td&gt;
&lt;td&gt;12 minutes&lt;/td&gt;
&lt;td&gt;3 minutes&lt;/td&gt;
&lt;td&gt;-75%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PRs Reviewed/Week&lt;/td&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;td&gt;0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bugs Caught&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;+25%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mental Fatigue Score&lt;/td&gt;
&lt;td&gt;8/10&lt;/td&gt;
&lt;td&gt;3/10&lt;/td&gt;
&lt;td&gt;-62%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The "Bugs Caught" number went up slightly. Why? Because the AI caught two edge cases in database migrations that I had glossed over. It noticed a missing index on a new query.&lt;/p&gt;

&lt;p&gt;I probably would have caught it later. But catching it in review saved us a hotfix deployment.&lt;/p&gt;

&lt;p&gt;The mental fatigue score is subjective. But I can tell you I don't dread opening GitHub anymore.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where It Failed (And How I Fixed It)
&lt;/h2&gt;

&lt;p&gt;It wasn't all smooth sailing.&lt;/p&gt;

&lt;p&gt;In week two, the AI started hallucinating imports. It suggested adding &lt;code&gt;import os&lt;/code&gt; when &lt;code&gt;os&lt;/code&gt; was already imported at the top of the file. It couldn't see the full file context, only the diff.&lt;/p&gt;

&lt;p&gt;This was a classic context window problem.&lt;/p&gt;

&lt;p&gt;I fixed it by changing the trigger. Instead of sending just the diff, I now send the diff plus the full file content for any file changed by more than 50%.&lt;/p&gt;

&lt;p&gt;This increased token usage by about 30%. But it reduced false positives by 90%.&lt;/p&gt;

&lt;p&gt;Another failure was tone. The AI was rude. It said things like "This is stupid code."&lt;/p&gt;

&lt;p&gt;I had to add a negative constraint to the prompt: "Never use condescending language. Be professional."&lt;/p&gt;

&lt;h2&gt;
  
  
  Developers
&lt;/h2&gt;

&lt;p&gt;💡 &lt;strong&gt;Further Reading&lt;/strong&gt;: I experiment with AI automation and open-source tools. Find more guides at &lt;a href="https://www.pistack.xyz" rel="noopener noreferrer"&gt;Pi Stack&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>GitHub Copilot Just Changed — Here's What It Means for Devs in 2026</title>
      <dc:creator>Hopkins Jesse</dc:creator>
      <pubDate>Sun, 17 May 2026 06:01:51 +0000</pubDate>
      <link>https://forem.com/hopkins_jesse_cdb68cfa22c/github-copilot-just-changed-heres-what-it-means-for-devs-in-2026-4e08</link>
      <guid>https://forem.com/hopkins_jesse_cdb68cfa22c/github-copilot-just-changed-heres-what-it-means-for-devs-in-2026-4e08</guid>
      <description>&lt;p&gt;I stared at my terminal for ten minutes yesterday. Not because I was stuck on a bug. But because GitHub Copilot refused to write a single line of code for me.&lt;/p&gt;

&lt;p&gt;It wasn’t broken. It was working exactly as intended.&lt;/p&gt;

&lt;p&gt;The new "Context-Aware Guardrails" update rolled out on March 12, 2026. It changes how the AI interacts with our codebases. Specifically, it stops suggesting code when it detects high-risk patterns or insufficient context.&lt;/p&gt;

&lt;p&gt;For the first week, I hated it. My velocity dropped by 40%. I felt like I was typing with one hand tied behind my back.&lt;/p&gt;

&lt;p&gt;Now, three weeks later, my pull request rejection rate has fallen from 15% to 2%. The trade-off is real. We are losing speed to gain accuracy.&lt;/p&gt;

&lt;p&gt;Here is what actually happened during the migration and why you need to adjust your workflow now.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Silent Update That Broke My Flow
&lt;/h2&gt;

&lt;p&gt;I didn’t read the changelog. Nobody does. I just opened VS Code on a Tuesday morning and started building a new authentication middleware for our internal dashboard.&lt;/p&gt;

&lt;p&gt;Usually, I type &lt;code&gt;// create jwt verifier&lt;/code&gt; and wait for the ghost text to appear. It used to generate about 20 lines of boilerplate instantly.&lt;/p&gt;

&lt;p&gt;This time? Nothing.&lt;/p&gt;

&lt;p&gt;I hit &lt;code&gt;Cmd+K&lt;/code&gt; to force a chat generation. The response was blunt.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Insufficient context regarding security protocols for this module. Please define the expected token structure and error handling strategy before generating implementation details."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I thought it was a glitch. I restarted VS Code. I checked my internet connection. I even logged out and back in.&lt;/p&gt;

&lt;p&gt;Same result.&lt;/p&gt;

&lt;p&gt;I dug into the release notes later that evening. Microsoft had partnered with several major security firms to embed static analysis directly into the suggestion engine. If the AI detects that you are writing security-sensitive code without prior definition of constraints, it blocks the suggestion.&lt;/p&gt;

&lt;p&gt;They call it "Preventative Context Enforcement." I call it a productivity hurdle. But the data suggests they might be right.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data: Speed vs. Security
&lt;/h2&gt;

&lt;p&gt;I tracked my metrics for the month of March 2026. I compared my output against my February baseline. I wanted to see if this was just annoyance or if there was actual value.&lt;/p&gt;

&lt;p&gt;I used a simple script to log my keystrokes, acceptance rates, and PR feedback loops.&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;Feb 2026 (Pre-Update)&lt;/th&gt;
&lt;th&gt;Mar 2026 (Post-Update)&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lines of Code Generated&lt;/td&gt;
&lt;td&gt;12,400&lt;/td&gt;
&lt;td&gt;8,900&lt;/td&gt;
&lt;td&gt;-28%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI Suggestion Acceptance Rate&lt;/td&gt;
&lt;td&gt;65%&lt;/td&gt;
&lt;td&gt;42%&lt;/td&gt;
&lt;td&gt;-23%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time Spent Refactoring&lt;/td&gt;
&lt;td&gt;14 hours&lt;/td&gt;
&lt;td&gt;6 hours&lt;/td&gt;
&lt;td&gt;-57%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security Vulnerabilities Found in QA&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;-87%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avg PR Review Time&lt;/td&gt;
&lt;td&gt;4.5 hours&lt;/td&gt;
&lt;td&gt;2.1 hours&lt;/td&gt;
&lt;td&gt;-53%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The drop in generated lines looks bad at first glance. I am writing more code manually. My fingers hurt more at the end of the day.&lt;/p&gt;

&lt;p&gt;But look at the refactoring time. I spent less than half the time fixing my own mess. The AI stopped giving me mediocre code that looked correct but failed edge cases.&lt;/p&gt;

&lt;p&gt;The security vulnerability metric is the kicker. We caught one minor issue in QA. Last month, we had eight. One of those was a potential injection flaw that would have cost us days of patching.&lt;/p&gt;

&lt;p&gt;The AI isn't writing less code because it's dumber. It's writing less because it's refusing to guess.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Adapted My Workflow
&lt;/h2&gt;

&lt;p&gt;I couldn't keep fighting the tool. I had to change how I prompt it. The era of lazy prompting is over. You can no longer throw a comment at the screen and hope for magic.&lt;/p&gt;

&lt;p&gt;I developed a three-step process for complex tasks.&lt;/p&gt;

&lt;p&gt;First, I define the interface. I write the types, the inputs, and the outputs. I do not ask for implementation yet.&lt;/p&gt;

&lt;p&gt;Second, I describe the constraints. I explicitly state what libraries are allowed, what error formats we use, and any security requirements.&lt;/p&gt;

&lt;p&gt;Third, I ask for the implementation.&lt;/p&gt;

&lt;p&gt;Here is an example of what works now versus what used to work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Old Approach (Failed):&lt;/strong&gt;&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;// TODO: verify user token from header&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;New Approach (Successful):&lt;/strong&gt;&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="cm"&gt;/**
 * Verifies JWT token from Authorization header.
 * 
 * Constraints:
 * - Use 'jose' library for verification
 * - Reject tokens expired &amp;gt; 5 minutes ago
 * - Return custom AppError on failure, do not throw
 * - Validate 'aud' claim matches ENV variable AUDIENCE_ID
 */&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;verifyToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserPayload&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Implementation requested here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I provide that level of detail, Copilot generates the code instantly. It passes our linter. It passes our security scan. It works.&lt;/p&gt;

&lt;p&gt;The extra two minutes I spend writing the JSDoc comment saves me twenty minutes of debugging later.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hidden Cost of "Smart" Tools
&lt;/h2&gt;

&lt;p&gt;There is a downside. The cognitive load is higher. I am thinking more about architecture before I write code. This is good for senior developers. It is exhausting for juniors.&lt;/p&gt;

&lt;h2&gt;
  
  
  I mentored two junior devs on my team during this transition. They struggled. They relied on Copilot to teach them
&lt;/h2&gt;

&lt;p&gt;💡 &lt;strong&gt;Further Reading&lt;/strong&gt;: I experiment with AI automation and open-source tools. Find more guides at &lt;a href="https://www.pistack.xyz" rel="noopener noreferrer"&gt;Pi Stack&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>news</category>
      <category>developer</category>
      <category>tech</category>
    </item>
    <item>
      <title>5 Mistakes I Made Building an AI Code Review Bot</title>
      <dc:creator>Hopkins Jesse</dc:creator>
      <pubDate>Sun, 17 May 2026 06:01:39 +0000</pubDate>
      <link>https://forem.com/hopkins_jesse_cdb68cfa22c/5-mistakes-i-made-building-an-ai-code-review-bot-5527</link>
      <guid>https://forem.com/hopkins_jesse_cdb68cfa22c/5-mistakes-i-made-building-an-ai-code-review-bot-5527</guid>
      <description>&lt;p&gt;I spent three months building "ReviewBot," an automated code reviewer for our internal monorepo. The goal was simple. Catch bugs before they hit production. Reduce the cognitive load on senior engineers during pull requests.&lt;/p&gt;

&lt;p&gt;It sounded great on paper. In practice, it was a disaster for the first six weeks.&lt;/p&gt;

&lt;p&gt;We are in 2026. Large Language Models are cheap and fast. You might think integrating one into a CI/CD pipeline is trivial. It is not. The complexity shifts from model selection to context management and latency handling.&lt;/p&gt;

&lt;p&gt;Here are the five specific mistakes I made. I hope you can skip the pain I went through.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistake 1: Ignoring Context Window Costs
&lt;/h2&gt;

&lt;p&gt;My first version sent the entire file history to the model. I thought more context meant better accuracy. I was wrong. It just meant higher bills and slower responses.&lt;/p&gt;

&lt;p&gt;We use a mid-tier proprietary model that charges per token. Our average pull request involves touching 15 files. Each file has about 300 lines of code. Sending all that data added up quickly.&lt;/p&gt;

&lt;p&gt;By week two, our API bill jumped from $50 to $420. That is unsustainable for an internal tool.&lt;/p&gt;

&lt;p&gt;I realized we only needed the diff and the immediate surrounding functions. The rest is noise. The model does not need to know the contents of &lt;code&gt;utils/dateFormatter.js&lt;/code&gt; when you are changing &lt;code&gt;components/LoginButton.tsx&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I switched to a tree-sitter based parser. It extracts only the relevant abstract syntax tree nodes. This reduced our token usage by 85%.&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;V1 (Full File)&lt;/th&gt;
&lt;th&gt;V2 (AST Extract)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Avg Tokens per PR&lt;/td&gt;
&lt;td&gt;12,500&lt;/td&gt;
&lt;td&gt;1,800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost per PR&lt;/td&gt;
&lt;td&gt;$0.04&lt;/td&gt;
&lt;td&gt;$0.005&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latency (p95)&lt;/td&gt;
&lt;td&gt;4.2s&lt;/td&gt;
&lt;td&gt;1.1s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The cost drop was immediate. The speed improvement was even better. Developers stopped complaining about waiting for checks to pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistake 2: Treating LLM Output as Deterministic
&lt;/h2&gt;

&lt;p&gt;I wrote my initial assertion tests assuming the AI would always return valid JSON. I asked it to output a structured report with severity levels and suggested fixes.&lt;/p&gt;

&lt;p&gt;For the first 50 runs, it worked perfectly. Then, on a Tuesday morning, it broke the entire pipeline.&lt;/p&gt;

&lt;p&gt;The model decided to be chatty. It added a preamble like "Here is your code review:" before the JSON block. My parser crashed. The CI check failed. Developers were blocked from merging critical hotfixes.&lt;/p&gt;

&lt;p&gt;I had to manually restart the pipeline for three different teams. It was embarrassing.&lt;/p&gt;

&lt;p&gt;LLMs are probabilistic. They do not guarantee format consistency unless you force it. I stopped relying on raw text parsing. Instead, I implemented a retry mechanism with strict schema validation using Zod.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zod&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;ReviewSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;low&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;medium&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;high&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;suggestion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;parseReview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawOutput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Strip markdown code blocks if present&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cleanJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rawOutput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/``&lt;/span&gt;&lt;span class="err"&gt;`
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;endraw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="s2"&gt;```/g, '');
    const parsed = JSON.parse(cleanJson);
    return ReviewSchema.parse(parsed);
  } catch (error) {
    console.error('Failed to parse AI output', error);
    throw new Error('Invalid review format');
  }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This change did not fix the model's tendency to ramble. It just handled the failure gracefully. We now log the bad output for fine-tuning later. The pipeline keeps moving.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistake 3: Over-Engineering the Agent Loop
&lt;/h2&gt;

&lt;p&gt;I got excited about agentic workflows. I built a system where the bot could "think" step-by-step. It would analyze the code, search documentation, then propose a fix.&lt;/p&gt;

&lt;p&gt;This added 15 seconds to every comment. In a fast-moving team, 15 seconds feels like an hour.&lt;/p&gt;

&lt;p&gt;Developers do not want a philosopher. They want a linter with opinions. Most of the time, the issues are simple. Unused variables. Type mismatches. Missing error handling.&lt;/p&gt;

&lt;p&gt;These do not require a multi-step reasoning loop. They require pattern matching.&lt;/p&gt;

&lt;p&gt;I stripped out the agent logic for 90% of cases. Now, the system uses a lightweight model for basic syntax and style checks. It only triggers the heavy reasoning model if it detects complex logic changes or security vulnerabilities.&lt;/p&gt;

&lt;p&gt;This hybrid approach cut our average response time from 18 seconds to 3 seconds. The quality of feedback did not drop. In fact, it improved because the bot was less likely to hallucinate complex solutions for simple problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistake 4: Neglecting User Feedback Loops
&lt;/h2&gt;

&lt;p&gt;I assumed developers would love the tool. I did not build a way for them to tell me when it was wrong.&lt;/p&gt;

&lt;p&gt;For the first month, I had no idea if the suggestions were useful. I only saw usage metrics. I saw that people were reading the comments. I did not know if they were acting on them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then I noticed a pattern. Senior engineers were ignoring 80% of the high-severity warnings. Why? Because the bot was flagging intentional architectural decisions as errors.
&lt;/h2&gt;

&lt;p&gt;💡 &lt;strong&gt;Further Reading&lt;/strong&gt;: I experiment with AI automation and open-source tools. Find more guides at &lt;a href="https://www.pistack.xyz" rel="noopener noreferrer"&gt;Pi Stack&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>developer</category>
      <category>experience</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Secret AI Debugging Workflow Nobody Uses (But Should)</title>
      <dc:creator>Hopkins Jesse</dc:creator>
      <pubDate>Sat, 16 May 2026 06:01:06 +0000</pubDate>
      <link>https://forem.com/hopkins_jesse_cdb68cfa22c/the-secret-ai-debugging-workflow-nobody-uses-but-should-8em</link>
      <guid>https://forem.com/hopkins_jesse_cdb68cfa22c/the-secret-ai-debugging-workflow-nobody-uses-but-should-8em</guid>
      <description>&lt;p&gt;I spent three hours last Tuesday chasing a race condition in a Next.js 15 app.&lt;/p&gt;

&lt;p&gt;The error was intermittent. It only happened when I refreshed the page exactly 4 seconds after login.&lt;/p&gt;

&lt;p&gt;My usual workflow involves console logging, guessing, and praying. It failed me again.&lt;/p&gt;

&lt;p&gt;Then I remembered a feature buried in the VS Code settings that most developers ignore.&lt;/p&gt;

&lt;p&gt;It is not about generating code. It is about understanding execution flow.&lt;/p&gt;

&lt;p&gt;This is the "Trace-First" debugging workflow.&lt;/p&gt;

&lt;p&gt;Most devs use AI to write boilerplate. I started using it to map state changes.&lt;/p&gt;

&lt;p&gt;The results were shocking. My debug time dropped from hours to minutes.&lt;/p&gt;

&lt;p&gt;Here is exactly how I set it up, why it works, and where I messed up initially.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Standard AI Debugging
&lt;/h2&gt;

&lt;p&gt;We all know the drill.&lt;/p&gt;

&lt;p&gt;You paste an error message into Cursor or Copilot Chat.&lt;/p&gt;

&lt;p&gt;The AI suggests a fix. You apply it. The error persists.&lt;/p&gt;

&lt;p&gt;You paste the new error. The AI suggests another fix.&lt;/p&gt;

&lt;p&gt;This loop is expensive. It wastes tokens and, more importantly, mental energy.&lt;/p&gt;

&lt;p&gt;In 2026, LLMs are great at syntax. They are still mediocre at context.&lt;/p&gt;

&lt;p&gt;They do not know what your &lt;code&gt;useEffect&lt;/code&gt; did three renders ago.&lt;/p&gt;

&lt;p&gt;They do not see the network request that failed silently in the background.&lt;/p&gt;

&lt;p&gt;I realized I was asking the wrong question.&lt;/p&gt;

&lt;p&gt;Instead of asking "How do I fix this?", I needed to ask "What actually happened?"&lt;/p&gt;

&lt;p&gt;I needed a timeline. A forensic audit of my application state.&lt;/p&gt;

&lt;p&gt;Manual logging is too slow. Setting breakpoints is too disruptive for async flows.&lt;/p&gt;

&lt;p&gt;I needed a way to dump structured execution data that an AI could actually read.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Trace-First Setup
&lt;/h2&gt;

&lt;p&gt;The core idea is simple.&lt;/p&gt;

&lt;p&gt;Create a middleware or wrapper that logs every significant event to a structured JSON file.&lt;/p&gt;

&lt;p&gt;Then, feed that JSON file to your AI assistant.&lt;/p&gt;

&lt;p&gt;Do not paste screenshots. Do not paste vague descriptions.&lt;/p&gt;

&lt;p&gt;Paste the raw data trace.&lt;/p&gt;

&lt;p&gt;I built a lightweight tracer for my React Server Components.&lt;/p&gt;

&lt;p&gt;It hooks into the fetch calls and state updates.&lt;/p&gt;

&lt;p&gt;Here is the basic structure I used. It is not pretty, but it works.&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;// lib/tracer.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&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;TRACE_FILE&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;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.trace&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;debug-log.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;logTrace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;any&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;entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;eventId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// Append to file safely&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;existsSync&lt;/span&gt;&lt;span class="p"&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;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TRACE_FILE&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdirSync&lt;/span&gt;&lt;span class="p"&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;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TRACE_FILE&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;recursive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;existing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;existsSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TRACE_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TRACE_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="nx"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TRACE_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Trace write failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wrapped my API calls with this function.&lt;/p&gt;

&lt;p&gt;Every time a fetch started or finished, it logged the payload and the response status.&lt;/p&gt;

&lt;p&gt;When a component re-rendered, I logged the props it received.&lt;/p&gt;

&lt;p&gt;This created a chronological history of the bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Workflow in Action
&lt;/h2&gt;

&lt;p&gt;Last week, I had a bug where user preferences were not saving.&lt;/p&gt;

&lt;p&gt;The UI showed the update. The database remained unchanged.&lt;/p&gt;

&lt;p&gt;Old me would have checked the API route. Then the Prisma schema. Then the network tab.&lt;/p&gt;

&lt;p&gt;New me ran the app, reproduced the bug, and stopped.&lt;/p&gt;

&lt;p&gt;I opened the &lt;code&gt;.trace/debug-log.json&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;It had 42 entries. Too many for a human to scan quickly.&lt;/p&gt;

&lt;p&gt;Perfect for an LLM.&lt;/p&gt;

&lt;p&gt;I opened Copilot Chat and typed:&lt;/p&gt;

&lt;p&gt;"Analyze this trace log. Find where the state diverges from the expected database write. Look for missing fields or silent failures."&lt;/p&gt;

&lt;p&gt;I pasted the JSON.&lt;/p&gt;

&lt;p&gt;The AI responded in 8 seconds.&lt;/p&gt;

&lt;p&gt;It pointed out entry #38.&lt;/p&gt;

&lt;p&gt;The frontend sent a &lt;code&gt;PATCH&lt;/code&gt; request with &lt;code&gt;theme: 'dark'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The backend received it. But entry #39 showed the validation layer stripping the &lt;code&gt;theme&lt;/code&gt; field because it was not in the allowed enum list.&lt;/p&gt;

&lt;p&gt;The enum had been updated in the database but not in the Zod schema.&lt;/p&gt;

&lt;p&gt;The error was silent. The API returned 200 OK with the original data.&lt;/p&gt;

&lt;p&gt;Without the trace, I would never have seen that the payload was modified mid-flight.&lt;/p&gt;

&lt;p&gt;With the trace, the AI spotted the schema mismatch instantly.&lt;/p&gt;

&lt;p&gt;I fixed the Zod schema. The bug vanished.&lt;/p&gt;

&lt;p&gt;Total time: 12 minutes.&lt;/p&gt;

&lt;p&gt;Previous average time for this type of bug: 2.5 hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Beats Breakpoints
&lt;/h2&gt;

&lt;p&gt;Breakpoints pause time. They are static.&lt;/p&gt;

&lt;p&gt;Async bugs are dynamic. They happen across time and threads.&lt;/p&gt;

&lt;p&gt;A breakpoint tells you the state &lt;em&gt;now&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;A trace tells you the state &lt;em&gt;then&lt;/em&gt;, &lt;em&gt;before&lt;/em&gt;, and &lt;em&gt;after&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;AI models excel at pattern matching in text.&lt;/p&gt;

&lt;p&gt;JSON is text. It is structured, predictable text.&lt;/p&gt;

&lt;p&gt;When you give an AI a trace, you are giving it the full context window.&lt;/p&gt;

&lt;p&gt;You are removing the guesswork.&lt;/p&gt;

&lt;p&gt;I tested this on five different bugs over two weeks.&lt;/p&gt;

&lt;p&gt;Here is the breakdown:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bug Type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;💡 &lt;strong&gt;Further Reading&lt;/strong&gt;: I experiment with AI automation and open-source tools. Find more guides at &lt;a href="https://www.pistack.xyz" rel="noopener noreferrer"&gt;Pi Stack&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>workflow</category>
      <category>tutorial</category>
      <category>developer</category>
    </item>
    <item>
      <title>I Let AI Refactor My Legacy Code for 30 Days — The Results Shocked Me</title>
      <dc:creator>Hopkins Jesse</dc:creator>
      <pubDate>Sat, 16 May 2026 06:00:55 +0000</pubDate>
      <link>https://forem.com/hopkins_jesse_cdb68cfa22c/i-let-ai-refactor-my-legacy-code-for-30-days-the-results-shocked-me-2npj</link>
      <guid>https://forem.com/hopkins_jesse_cdb68cfa22c/i-let-ai-refactor-my-legacy-code-for-30-days-the-results-shocked-me-2npj</guid>
      <description>&lt;p&gt;I have a monolith. It is a Rails application from 2018 that handles billing for a mid-sized SaaS. We call it "The Beast" internally because it bites back whenever you try to touch the payment logic.&lt;/p&gt;

&lt;p&gt;For years, my strategy was simple. If it works, do not touch it. If it breaks, patch it with duct tape and prayers. This approach worked until last month when we needed to migrate our payment provider. The existing code was so tangled that estimating the work felt like guessing the weight of a cloud.&lt;/p&gt;

&lt;p&gt;I decided to run an experiment. I would let an autonomous AI agent handle the refactoring of our billing module for 30 days. Not just code completion. Full structural refactoring.&lt;/p&gt;

&lt;p&gt;My goal was not perfection. I wanted to see if AI could reduce the cognitive load of understanding legacy spaghetti. I set strict guardrails. The AI could propose changes, but I had to approve every pull request.&lt;/p&gt;

&lt;p&gt;Here is what happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup: Guardrails Over Trust
&lt;/h2&gt;

&lt;p&gt;I did not just point Cursor or Copilot at the codebase and walk away. That is how you get security vulnerabilities and infinite loops.&lt;/p&gt;

&lt;p&gt;I used a local LLM setup with a specialized agent framework. I chose this over cloud APIs because our billing data contains PII. I could not risk sending customer credit card tokens to a third-party server.&lt;/p&gt;

&lt;p&gt;The stack looked like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model&lt;/strong&gt;: Llama-3-70b-Instruct (quantized, running on local A100s)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framework&lt;/strong&gt;: LangGraph for state management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing&lt;/strong&gt;: RSpec suite with 94% coverage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guardrail&lt;/strong&gt;: Every change required passing the full test suite before I even saw the diff.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I gave the agent one instruction. "Refactor the &lt;code&gt;PaymentProcessor&lt;/code&gt; class to use the Strategy Pattern. Keep all public methods identical. Do not change business logic."&lt;/p&gt;

&lt;p&gt;Simple, right? Wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Week 1: The Hallucination Phase
&lt;/h2&gt;

&lt;p&gt;The first three days were painful. The agent kept trying to import libraries that did not exist. It invented a gem called &lt;code&gt;active_billing_strategy&lt;/code&gt; and tried to bundle install it.&lt;/p&gt;

&lt;p&gt;I spent four hours just correcting its understanding of our Gemfile.&lt;/p&gt;

&lt;p&gt;On day four, it produced its first valid pull request. It extracted the Stripe logic into a separate class. The code looked clean. Too clean.&lt;/p&gt;

&lt;p&gt;I reviewed the diff. It had removed a critical idempotency check. This check prevented double-charging customers if the webhook fired twice. The tests passed because our test fixtures did not cover concurrent webhook delivery.&lt;/p&gt;

&lt;p&gt;This was a wake-up call. The AI optimized for readability, not correctness. It missed the subtle side effects that only exist in production traffic.&lt;/p&gt;

&lt;p&gt;I added a new rule. "Do not remove any lines containing &lt;code&gt;idempotency_key&lt;/code&gt; without explicit human comment approval."&lt;/p&gt;

&lt;h2&gt;
  
  
  Week 2: Finding the Hidden Bugs
&lt;/h2&gt;

&lt;p&gt;After fixing the guardrails, things improved. The agent started identifying dead code. It found three methods in the &lt;code&gt;Invoice&lt;/code&gt; model that had not been called since 2019.&lt;/p&gt;

&lt;p&gt;It also spotted a N+1 query problem in the invoice generation loop. I had missed this for five years. The AI suggested adding &lt;code&gt;.includes(:line_items)&lt;/code&gt; to the ActiveRecord query.&lt;/p&gt;

&lt;p&gt;This single change reduced our invoice generation time from 4 seconds to 200 milliseconds.&lt;/p&gt;

&lt;p&gt;I felt a mix of pride and shame. Pride that the system was faster. Shame that I had not caught this earlier.&lt;/p&gt;

&lt;p&gt;Here is the data from our staging environment during week two:&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;Before AI Refactor&lt;/th&gt;
&lt;th&gt;After AI Refactor&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Avg Invoice Gen Time&lt;/td&gt;
&lt;td&gt;4.1s&lt;/td&gt;
&lt;td&gt;0.2s&lt;/td&gt;
&lt;td&gt;-95%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cyclomatic Complexity&lt;/td&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;-57%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test Suite Duration&lt;/td&gt;
&lt;td&gt;12m 30s&lt;/td&gt;
&lt;td&gt;11m 45s&lt;/td&gt;
&lt;td&gt;-6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Human Review Time&lt;/td&gt;
&lt;td&gt;0m&lt;/td&gt;
&lt;td&gt;45m/day&lt;/td&gt;
&lt;td&gt;+45m&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The test suite duration did not drop much because the AI added more granular tests. It believed that more tests equaled better safety. In this case, it was right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Week 3: The Style War
&lt;/h2&gt;

&lt;p&gt;By week three, the code looked different. The AI preferred functional styles over object-oriented patterns. It replaced many &lt;code&gt;if/else&lt;/code&gt; blocks with hash lookups.&lt;/p&gt;

&lt;p&gt;Ruby developers know this as a stylistic choice. But our team had conventions. We used classes for complex logic. The AI kept turning classes into hashes.&lt;/p&gt;

&lt;p&gt;I had to spend time teaching the agent our style guide. I fed it our top 10 most recent approved PRs as few-shot examples.&lt;/p&gt;

&lt;p&gt;Once it understood the pattern, the quality jumped. It stopped fighting our conventions. It started writing code that looked like I wrote it, but cleaner.&lt;/p&gt;

&lt;p&gt;It also documented everything. Every method got a YARD docstring. Most were generic, but some were surprisingly insightful. It explained why a specific regex was used for email validation. I had forgotten why we used that specific regex. The AI inferred it from the test cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Week 4: The Final Push
&lt;/h2&gt;

&lt;p&gt;In the final week, I asked the agent to tackle the hardest part. The tax calculation logic. This code had 15 nested conditionals based on state, country, and product type.&lt;/p&gt;

&lt;p&gt;The agent proposed a complete rewrite using a rule engine pattern. It moved the logic out of code and into a YAML configuration file.&lt;/p&gt;

&lt;h2&gt;
  
  
  I was skeptical. Moving logic
&lt;/h2&gt;

&lt;p&gt;💡 &lt;strong&gt;Further Reading&lt;/strong&gt;: I experiment with AI automation and open-source tools. Find more guides at &lt;a href="https://www.pistack.xyz" rel="noopener noreferrer"&gt;Pi Stack&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>experiment</category>
      <category>productivity</category>
    </item>
    <item>
      <title>GitHub Copilot Just Changed — Here's What It Means for Devs in 2026</title>
      <dc:creator>Hopkins Jesse</dc:creator>
      <pubDate>Fri, 15 May 2026 06:01:39 +0000</pubDate>
      <link>https://forem.com/hopkins_jesse_cdb68cfa22c/github-copilot-just-changed-heres-what-it-means-for-devs-in-2026-5g24</link>
      <guid>https://forem.com/hopkins_jesse_cdb68cfa22c/github-copilot-just-changed-heres-what-it-means-for-devs-in-2026-5g24</guid>
      <description>&lt;p&gt;I woke up on March 14, 2026, to a Slack message from my CTO. It wasn't panic. It was confusion.&lt;/p&gt;

&lt;p&gt;Our team had just migrated to the new "Copilot Workspace" tier. The pricing jumped 40 percent per seat. We expected better code completion. We got an autonomous agent that could refactor entire modules without asking.&lt;/p&gt;

&lt;p&gt;I spent the last three weeks testing this update in production. I broke things. I fixed them. I learned where the hard limits are.&lt;/p&gt;

&lt;p&gt;If you are still treating AI as a fancy autocomplete tool, you are already behind. The model has shifted from assistance to agency. Here is what actually changed and how it impacts your daily workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The End of Line-by-Line Coding
&lt;/h2&gt;

&lt;p&gt;The biggest shift isn't speed. It is scope.&lt;/p&gt;

&lt;p&gt;In 2024, we asked Copilot to write a function. In 2026, we give it a Jira ticket ID and a branch name. It reads the context, checks existing patterns, and proposes a pull request.&lt;/p&gt;

&lt;p&gt;I tested this with a standard API endpoint migration. The task involved moving three services from REST to gRPC. Normally, this takes two days of boilerplate writing and proto file definition.&lt;/p&gt;

&lt;p&gt;I created a new branch. I typed one comment in the main entry file: &lt;code&gt;// Migrate user-service to gRPC following pattern in payment-service&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Copilot Workspace scanned our repo. It identified the &lt;code&gt;payment-service&lt;/code&gt; as the reference implementation. It generated the &lt;code&gt;.proto&lt;/code&gt; files. It updated the client calls. It even wrote the integration tests.&lt;/p&gt;

&lt;p&gt;It took 12 minutes.&lt;/p&gt;

&lt;p&gt;I reviewed the code for 45 minutes. I caught two logic errors in error handling. The rest merged cleanly.&lt;/p&gt;

&lt;p&gt;This changes the job description. You are no longer paid to type syntax. You are paid to verify logic. Your value lies in spotting the subtle bugs the agent misses, not in remembering semicolon placement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context Windows Are No Longer a Bottleneck
&lt;/h2&gt;

&lt;p&gt;Previous versions struggled with large codebases. They would hallucinate imports or miss dependencies if the file was too far from the current context.&lt;/p&gt;

&lt;p&gt;The 2026 update uses a localized vector index that updates in real-time. It understands your entire monorepo structure.&lt;/p&gt;

&lt;p&gt;I ran a test on our legacy authentication module. It spans 40 files and 12,000 lines of code. I asked the agent to add two-factor authentication support using TOTP.&lt;/p&gt;

&lt;p&gt;Old models would have guessed the database schema. This version queried our local TypeORM definitions. It matched the exact column types used in our &lt;code&gt;User&lt;/code&gt; entity.&lt;/p&gt;

&lt;p&gt;Here is the snippet it generated for the service layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./user.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;authenticator&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;otplib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Auth2FAService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;userService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;generateSecret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Agent correctly inferred we store secrets encrypted&lt;/span&gt;
    &lt;span class="c1"&gt;// and use the existing crypto utility&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;authenticator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateSecret&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateTwoFactorSecret&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;secret&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;secret&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the comment. The agent didn't just write code. It explained why it chose that specific method based on our existing &lt;code&gt;crypto&lt;/code&gt; utility. It read files I hadn't even opened.&lt;/p&gt;

&lt;p&gt;This reduces cognitive load significantly. You don't need to hold the entire architecture in your head. You just need to know if the agent's assumption about the architecture is correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  The New Risk Profile
&lt;/h2&gt;

&lt;p&gt;Autonomy introduces new failure modes. The most dangerous one is confidence drift.&lt;/p&gt;

&lt;p&gt;When an agent writes 90 percent of the code, developers tend to skim reviews. I caught myself doing this. I saw the tests passed. I saw the structure looked familiar. I almost merged a change that introduced a race condition.&lt;/p&gt;

&lt;p&gt;The agent had optimized for speed, not concurrency safety. It missed a lock on the database transaction because the reference file (&lt;code&gt;payment-service&lt;/code&gt;) used a synchronous queue, while our &lt;code&gt;user-service&lt;/code&gt; handles high-concurrency writes.&lt;/p&gt;

&lt;p&gt;This is a human error, amplified by AI. We trust the pattern match more than the logical reality.&lt;/p&gt;

&lt;p&gt;To combat this, I established a new rule for my team. If AI generates more than 50 percent of the diff, you must run the local integration suite manually. No skipping steps. No relying solely on CI.&lt;/p&gt;

&lt;p&gt;We also started tracking "AI-induced regressions." In February 2026, before the update, we had zero. In March, we had four. All were related to context misinterpretation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost vs. Velocity Data
&lt;/h2&gt;

&lt;p&gt;Is the 40 percent price hike worth it? I tracked our team's metrics for 30 days.&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;Feb 2026 (Legacy)&lt;/th&gt;
&lt;th&gt;Mar 2026 (Workspace)&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Avg PR Size (Lines)&lt;/td&gt;
&lt;td&gt;120&lt;/td&gt;
&lt;td&gt;450&lt;/td&gt;
&lt;td&gt;+275%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Review Time (Hours)&lt;/td&gt;
&lt;td&gt;2.5&lt;/td&gt;
&lt;td&gt;4.0&lt;/td&gt;
&lt;td&gt;+60%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bugs per PR&lt;/td&gt;
&lt;td&gt;0.8&lt;/td&gt;
&lt;td&gt;1.2&lt;/td&gt;
&lt;td&gt;+50%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Features Shipped&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;+58%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dev&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;💡 &lt;strong&gt;Further Reading&lt;/strong&gt;: I experiment with AI automation and open-source tools. Find more guides at &lt;a href="https://www.pistack.xyz" rel="noopener noreferrer"&gt;Pi Stack&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>news</category>
      <category>developer</category>
      <category>tech</category>
    </item>
    <item>
      <title>I Let AI Handle My PR Reviews for 30 Days — The Data Was Ugly</title>
      <dc:creator>Hopkins Jesse</dc:creator>
      <pubDate>Fri, 15 May 2026 06:01:28 +0000</pubDate>
      <link>https://forem.com/hopkins_jesse_cdb68cfa22c/i-let-ai-handle-my-pr-reviews-for-30-days-the-data-was-ugly-1odd</link>
      <guid>https://forem.com/hopkins_jesse_cdb68cfa22c/i-let-ai-handle-my-pr-reviews-for-30-days-the-data-was-ugly-1odd</guid>
      <description>&lt;p&gt;I stopped reviewing pull requests manually on March 1, 2026.&lt;/p&gt;

&lt;p&gt;It wasn’t a strategic decision born from a desire to optimize my workflow. It was pure exhaustion. I had spent the previous two weeks staring at diffs that ranged from trivial whitespace changes to complex refactors of our legacy authentication module. My brain felt like mush.&lt;/p&gt;

&lt;p&gt;So I hooked up "ReviewBot," a local LLM agent configured with our team’s style guide and security rules, to our GitHub repository. The promise was simple. It would catch syntax errors, flag potential security vulnerabilities, and enforce naming conventions. I would only step in for architectural decisions and logic validation.&lt;/p&gt;

&lt;p&gt;I expected to save ten hours a week. I expected cleaner code. What I got was a massive increase in velocity and a subtle, creeping decay in code quality that took me three weeks to notice.&lt;/p&gt;

&lt;p&gt;Here is exactly what happened during that month, including the metrics and the specific failure modes I encountered.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup and Initial Wins
&lt;/h2&gt;

&lt;p&gt;We are a team of six developers working on a mid-sized SaaS platform built with Next.js and Python. Our average PR size is about 400 lines of changed code. Before the experiment, the average time from "Open" to "Merged" was 18 hours. This included waiting for human reviewers who were often busy with their own tasks.&lt;/p&gt;

&lt;p&gt;I configured the agent using a custom system prompt. I fed it our &lt;code&gt;eslint&lt;/code&gt; config, our &lt;code&gt;pylint&lt;/code&gt; rules, and a markdown file containing our internal best practices. I also gave it read-only access to our recent commit history so it could understand context.&lt;/p&gt;

&lt;p&gt;The first week was fantastic.&lt;/p&gt;

&lt;p&gt;The bot caught three actual bugs. One was a missing null check in a TypeScript interface that would have caused a runtime crash. Another was a SQL injection vulnerability in a raw query that standard linters missed because the variable interpolation looked safe at a glance.&lt;/p&gt;

&lt;p&gt;My teammates loved it. They no longer had to wait for me to wake up and review their morning commits. The average merge time dropped to 4 hours. I felt like a genius. I thought I had solved the bottleneck.&lt;/p&gt;

&lt;p&gt;Then the second week started, and the noise began.&lt;/p&gt;

&lt;h2&gt;
  
  
  The False Positive Flood
&lt;/h2&gt;

&lt;p&gt;By day eight, the signal-to-noise ratio plummeted. The AI became overly cautious. It started flagging valid patterns as anti-patterns simply because they didn't match the most common examples in its training data.&lt;/p&gt;

&lt;p&gt;For instance, we use a specific pattern for error handling in our API routes that involves wrapping promises in a try-catch block with a custom logger. The AI flagged this as "redundant error handling" in twelve separate PRs. It suggested removing the try-catch blocks, which would have swallowed errors silently in production.&lt;/p&gt;

&lt;p&gt;I had to spend an hour each day dismissing these false positives. This wasn't saving time. It was shifting the workload from "reading code" to "managing the bot."&lt;/p&gt;

&lt;p&gt;Here is a breakdown of the comments generated by the AI during Week 2:&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;Count&lt;/th&gt;
&lt;th&gt;Action Required&lt;/th&gt;
&lt;th&gt;Time Spent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Valid Bug Catch&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Fix Code&lt;/td&gt;
&lt;td&gt;20 mins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Style Nitpick&lt;/td&gt;
&lt;td&gt;142&lt;/td&gt;
&lt;td&gt;Dismiss/Ignore&lt;/td&gt;
&lt;td&gt;35 mins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Incorrect Logic Flag&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;Explain to Dev&lt;/td&gt;
&lt;td&gt;45 mins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security False Alarm&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Verify &amp;amp; Dismiss&lt;/td&gt;
&lt;td&gt;15 mins&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Total time spent managing the bot: ~1 hour 55 minutes.&lt;/p&gt;

&lt;p&gt;This was worse than just reviewing the code myself. When I review code, I can skip the obvious stuff. The bot forced me to look at every single comment to ensure it wasn't hiding a real issue among the junk.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Subtle Quality Decay
&lt;/h2&gt;

&lt;p&gt;The real shock came in Week 4. I decided to do a random audit of the code merged during the experiment. I picked ten PRs that had been approved solely by the AI after I dismissed its initial comments.&lt;/p&gt;

&lt;p&gt;I found a pattern of "lazy" coding.&lt;/p&gt;

&lt;p&gt;Developers knew the AI wouldn't catch logical inefficiencies. It only checked for syntax and strict rule adherence. So, they started writing code that passed the checks but was structurally poor.&lt;/p&gt;

&lt;p&gt;One developer nested five levels of conditional statements because the AI didn't flag cyclomatic complexity unless it exceeded a hard threshold of 15. The code worked, but it was unreadable. Another developer duplicated a helper function across three files because the AI didn't have global context to see that the function already existed elsewhere.&lt;/p&gt;

&lt;p&gt;The AI was optimizing for compliance, not quality. And our team was optimizing for speed.&lt;/p&gt;

&lt;p&gt;I looked at our bug tracking system. In the month prior to the experiment, we had logged four minor bugs related to new features. In the 30 days of AI-only reviews, we logged eleven. Three of those were direct results of the logical gaps the AI missed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Human Element Is Not Replaceable
&lt;/h2&gt;

&lt;p&gt;I realized that code review is not just about finding bugs. It is about knowledge sharing. When I review a junior developer's code, I leave comments explaining why a certain approach is better. I link to documentation. I ask questions that force them to think about edge cases.&lt;/p&gt;

&lt;p&gt;The AI does none of this. It gives binary feedback. Pass or fail. Fix this line. Delete that import.&lt;/p&gt;

&lt;p&gt;Our junior developers stopped learning. They stopped asking questions. They just fixed what the bot told them to fix and merged the code. The mentorship loop was broken.&lt;/p&gt;

&lt;h2&gt;
  
  
  I also missed the context. The AI doesn't know that we are planning to deprecate the &lt;code&gt;UserService&lt;/code&gt; class next
&lt;/h2&gt;

&lt;p&gt;💡 &lt;strong&gt;Further Reading&lt;/strong&gt;: I experiment with AI automation and open-source tools. Find more guides at &lt;a href="https://www.pistack.xyz" rel="noopener noreferrer"&gt;Pi Stack&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>experiment</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
