<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Codzee.io</title>
    <description>The latest articles on DEV Community by Codzee.io (@codzee_io).</description>
    <link>https://dev.to/codzee_io</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4072305%2F64f26605-4d54-48b4-9777-a1c76eb52c93.png</url>
      <title>DEV Community: Codzee.io</title>
      <link>https://dev.to/codzee_io</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/codzee_io"/>
    <language>en</language>
    <item>
      <title>AI Code Review Pricing... What “Unlimited” Really Means...</title>
      <dc:creator>Codzee.io</dc:creator>
      <pubDate>Thu, 15 Oct 2026 10:23:24 +0000</pubDate>
      <link>https://dev.to/codzee_io/ai-code-review-pricing-what-unlimited-really-means-5e2b</link>
      <guid>https://dev.to/codzee_io/ai-code-review-pricing-what-unlimited-really-means-5e2b</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5h1cvy13sy4bw2ngnto5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5h1cvy13sy4bw2ngnto5.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.codzee.io/" rel="noopener noreferrer"&gt;AI code review &lt;/a&gt;tools can look affordable when you're comparing subscription prices. But what happens when your team starts reviewing hundreds of pull requests every month?&lt;/p&gt;

&lt;p&gt;The answer depends on how each tool measures usage, what happens when you reach its limits, and whether the feedback is actually useful.&lt;/p&gt;

&lt;p&gt;We examined the pricing models of several AI code review tools and analyzed 203 Greptile findings across 20 pull requests. Here's what the research revealed.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Every tool measures usage differently
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.codzee.io/" rel="noopener noreferrer"&gt;AI code review tools&lt;/a&gt; don't all use the same billing model.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Codzee:&lt;/strong&gt; Uses credits based on pull request size. Its Team plan adds 100 credits per developer to a shared team pool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Greptile:&lt;/strong&gt; Provides 50 credits per seat. Base reviews cost 1 credit, Plus reviews cost 3, and Apex reviews cost 10. Additional credits cost $1 each.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CodeRabbit:&lt;/strong&gt; Publishes hourly review limits of 5, 8, or 10 reviews depending on the paid plan. Independent analyses also report adaptive throttling and per-file overage charges, but these details require confirmation from CodeRabbit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bito:&lt;/strong&gt; Research identified a limit of 5,000 lines per seat per month, followed by a charge of $5 per additional 1,000 lines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important point is that a subscription price doesn't tell you the full cost. You need to understand what counts as usage and what happens when you exceed the allowance.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. What happens with 250 pull requests a month?
&lt;/h2&gt;

&lt;p&gt;Consider a modeled team of 10 developers reviewing 250 pull requests each month.&lt;/p&gt;

&lt;p&gt;The supplied research produced these estimates:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Estimated monthly cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CodeRabbit Essentials, 5 files per PR&lt;/td&gt;
&lt;td&gt;$553&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CodeRabbit Essentials, 8 files per PR&lt;/td&gt;
&lt;td&gt;$740&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CodeRabbit Essentials, 15 files per PR&lt;/td&gt;
&lt;td&gt;$1,178&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codzee Team, 250 medium-sized PRs&lt;/td&gt;
&lt;td&gt;$240&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These figures are based on the research's pricing assumptions, not actual customer invoices. The CodeRabbit estimates include reported overage charges that could not be confirmed through official rate-limit documentation.&lt;/p&gt;

&lt;p&gt;For Codzee, the scenario assumes that 250 medium-sized pull requests consume 500 of the team's 1,000 pooled credits.&lt;/p&gt;

&lt;p&gt;Why does the shared pool matter?&lt;/p&gt;

&lt;p&gt;Imagine one developer handles most of the team's pull requests while others contribute less frequently. With pooled credits, available capacity can be used across the team instead of remaining tied to individual allowances.&lt;/p&gt;

&lt;p&gt;That doesn't mean credits are unlimited. It means teams should evaluate how the allowance matches their actual workload.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. We examined 203 Greptile findings
&lt;/h2&gt;

&lt;p&gt;Pricing is only one part of evaluating an AI code reviewer. The quality of its findings matters just as much.&lt;/p&gt;

&lt;p&gt;For this part of the research, we retrieved Greptile's inline comments through the GitHub API across 20 pull requests in the &lt;code&gt;Johns223/John_Test_1&lt;/code&gt; benchmark repository.&lt;/p&gt;

&lt;p&gt;The 203 findings were distributed as follows:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Priority&lt;/th&gt;
&lt;th&gt;Findings&lt;/th&gt;
&lt;th&gt;Examples from the sample&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;P0&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Authentication weaknesses involving forgeable session tokens and unauthorized privileged access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P1&lt;/td&gt;
&lt;td&gt;154&lt;/td&gt;
&lt;td&gt;SQL injection, unsafe deserialization, path traversal, and monetary calculation errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P2&lt;/td&gt;
&lt;td&gt;47&lt;/td&gt;
&lt;td&gt;Test-quality issues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P3–P5&lt;/td&gt;
&lt;td&gt;0 observed&lt;/td&gt;
&lt;td&gt;Not present in the examined sample&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Twenty-six findings also carried a separate Security badge.&lt;/p&gt;

&lt;p&gt;One detail is worth noting: the P-labels represented priority or severity, not confidence. Greptile expressed confidence separately in its finding text.&lt;/p&gt;

&lt;p&gt;These results describe one repository and 20 pull requests. They do not establish Greptile's overall detection accuracy, and they are not a direct comparison with Codzee. We cannot use this sample to claim that one tool catches more bugs than another.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. What should developers evaluate?
&lt;/h2&gt;

&lt;p&gt;Before choosing an AI code review tool, consider three practical questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;How is usage measured?&lt;/strong&gt; Pull request size, review depth, lines of code, and review frequency can produce very different costs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What happens at the limit?&lt;/strong&gt; Check whether reviews stop, become throttled, or incur additional charges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Are the findings useful?&lt;/strong&gt; A large number of comments doesn't automatically mean better code review. Engineers still need to verify issues and decide which ones matter.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Codzee's shared-credit approach may suit teams with uneven workloads. Whether it is the right choice depends on pull request size, review volume, pricing, and performance on your own repositories.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;The best AI code review tool isn't necessarily the one advertising unlimited reviews or the lowest starting price.&lt;/p&gt;

&lt;p&gt;It's the one that fits your workflow, keeps usage understandable, and helps your team identify issues worth fixing.&lt;/p&gt;

&lt;p&gt;If you're exploring AI-powered code review for GitHub, you can learn more about &lt;a href="https://www.codzee.io/" rel="noopener noreferrer"&gt;Codzee&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>We Tested 4 AI Code Review Tools Against 18 Security and Logic Defects</title>
      <dc:creator>Codzee.io</dc:creator>
      <pubDate>Mon, 12 Oct 2026 09:51:02 +0000</pubDate>
      <link>https://dev.to/codzee_io/we-tested-4-ai-code-review-tools-against-18-security-and-logic-defects-3g61</link>
      <guid>https://dev.to/codzee_io/we-tested-4-ai-code-review-tools-against-18-security-and-logic-defects-3g61</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F27ntucqi6250pbu62u2e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F27ntucqi6250pbu62u2e.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Imagine opening a pull request that claims its tokens are secure, sessions expire correctly, and permissions fail safely.&lt;/p&gt;

&lt;p&gt;The description sounds reassuring. The implementation is vulnerable.&lt;/p&gt;

&lt;p&gt;Would your &lt;a href="https://www.codzee.io/" rel="noopener noreferrer"&gt;AI code review&lt;/a&gt; tool catch the difference?&lt;/p&gt;

&lt;p&gt;We tested four AI code review tools against exactly this scenario. The results revealed more than which tool detected the most defects. They also exposed gaps in test analysis, review visibility, and GitHub merge protection.&lt;/p&gt;

&lt;h2&gt;
  
  
  The benchmark setup
&lt;/h2&gt;

&lt;p&gt;For Round Ten of our AI code review benchmark, we compared:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.codzee.io/" rel="noopener noreferrer"&gt;Codzee&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Greptile&lt;/li&gt;
&lt;li&gt;CodeRabbit&lt;/li&gt;
&lt;li&gt;CodeAnt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We created a deliberately vulnerable sandbox pull request containing 18 planted security and logic defects across three source files.&lt;/p&gt;

&lt;p&gt;The pull request description made four security claims that the implementation contradicted. We also planted six defects in the test file and evaluated those separately.&lt;/p&gt;

&lt;p&gt;All four tools reviewed the same commit. We collected findings from GitHub's REST API and scored them against an answer key prepared before the branch was pushed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The results
&lt;/h2&gt;

&lt;p&gt;| AI code review tool | Defects detected |&lt;/p&gt;

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

&lt;p&gt;Codzee detected the most planted defects in this round. But the breakdown tells us more than the overall score.&lt;/p&gt;

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

&lt;p&gt;The six test defects were scored separately: Codzee found all six, while the other three tools found none.&lt;/p&gt;

&lt;h2&gt;
  
  
  The security claims looked convincing. The code wasn't.
&lt;/h2&gt;

&lt;p&gt;One claim in the pull request description said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tokens are signed and verified with HMAC-SHA256.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But the implementation used:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Buffer.from(payload).toString("base64")&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Base64 is an encoding, not a cryptographic signature. Without proper signing and verification, an attacker could construct a token containing an administrator role.&lt;/p&gt;

&lt;p&gt;Another claim said expired sessions were rejected. The implementation compared &lt;code&gt;expiresAt&lt;/code&gt; with &lt;code&gt;issuedAt&lt;/code&gt; instead of checking the current time. The expiration check therefore failed to enforce session expiry.&lt;/p&gt;

&lt;p&gt;We also found a permission fallback that granted read and write access to unknown roles, even though the description promised a deny-by-default model.&lt;/p&gt;

&lt;p&gt;These were not cosmetic code review comments. They were direct contradictions between the stated security requirements and the implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  A surprising result: the description didn't fool the tools
&lt;/h2&gt;

&lt;p&gt;All four tools detected at least three of the four defects that the pull request description claimed were already handled.&lt;/p&gt;

&lt;p&gt;Three competing tools explicitly connected their findings to the author's claims. Codzee detected all four defects but did not explicitly point out that the description itself was inaccurate.&lt;/p&gt;

&lt;p&gt;That distinction matters during a real pull request review.&lt;/p&gt;

&lt;p&gt;Compare these two findings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"The secret comparison is not timing-safe."&lt;/li&gt;
&lt;li&gt;"The secret comparison is not timing-safe, despite the security property claimed in the pull request."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second gives the engineering team more context. It explains not only what is wrong, but why the implementation fails to meet its own stated requirements.&lt;/p&gt;

&lt;p&gt;None of the four tools explicitly recommended correcting the misleading description before merging.&lt;/p&gt;

&lt;h2&gt;
  
  
  The defects that weren't mentioned
&lt;/h2&gt;

&lt;p&gt;The largest difference appeared in the ten additional security defects that the author never mentioned.&lt;/p&gt;

&lt;p&gt;Codzee detected all ten. Greptile detected seven, CodeRabbit five, and CodeAnt four.&lt;/p&gt;

&lt;p&gt;The defects included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A missing ownership check that allowed access to another user's booking.&lt;/li&gt;
&lt;li&gt;A permission check that treated a role containing &lt;code&gt;admin&lt;/code&gt; as an administrator role.&lt;/li&gt;
&lt;li&gt;A permission cache that was never invalidated after privilege changes.&lt;/li&gt;
&lt;li&gt;Session IDs generated using &lt;code&gt;Math.random()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A hardcoded fallback secret.&lt;/li&gt;
&lt;li&gt;Login error messages that revealed whether a username existed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Codzee was the only tool to detect the final three issues.&lt;/p&gt;

&lt;p&gt;This is an important dimension when evaluating AI code review tools: can they identify problems beyond the ones suggested by the pull request description?&lt;/p&gt;

&lt;h2&gt;
  
  
  Why test analysis matters
&lt;/h2&gt;

&lt;p&gt;Codzee also detected all six planted test defects.&lt;/p&gt;

&lt;p&gt;Two were especially concerning. One test expected a session to be created from an &lt;code&gt;x-user-id&lt;/code&gt; header. Another expected the application to return a booking belonging to a different user.&lt;/p&gt;

&lt;p&gt;These tests treated vulnerable behavior as correct.&lt;/p&gt;

&lt;p&gt;A test suite can provide false confidence when it validates the wrong behavior. Reviewing application code without examining its tests can leave that problem undiscovered.&lt;/p&gt;

&lt;p&gt;This result is promising, but it comes from six deliberately planted test defects in one sandbox. It should not be interpreted as proof that Codzee will find every test-related issue in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  The result wasn't perfect, and that matters
&lt;/h2&gt;

&lt;p&gt;Codzee missed one defect: &lt;code&gt;logout()&lt;/code&gt; did not invalidate the token. A session that appeared to be logged out could remain valid.&lt;/p&gt;

&lt;p&gt;The experiment also revealed problems in Codzee's own review workflow.&lt;/p&gt;

&lt;p&gt;Of its 29 findings, only 10 appeared as inline comments. The remaining 19 were placed in a collapsed block, making some important findings harder to notice.&lt;/p&gt;

&lt;p&gt;More seriously, Codzee reported ten Critical findings but returned a neutral GitHub check conclusion. Its legacy commit status simultaneously reported success.&lt;/p&gt;

&lt;p&gt;This creates a gap between detecting a vulnerability and enforcing a security policy. If a team expects Critical findings to block merges, a neutral check does not meet that expectation.&lt;/p&gt;

&lt;p&gt;We identified three priorities:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make Critical findings enforceable through configurable GitHub check behavior.&lt;/li&gt;
&lt;li&gt;Improve the visibility of findings, especially those that appear outside inline comments.&lt;/li&gt;
&lt;li&gt;Include the pull request title and description in the review context so the tool can explicitly flag misleading security claims.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What this benchmark tells engineering teams
&lt;/h2&gt;

&lt;p&gt;This experiment involved one deliberately constructed sandbox pull request, not thousands of production changes. It cannot establish that one tool will always outperform another.&lt;/p&gt;

&lt;p&gt;It does, however, suggest that evaluating AI code review requires more than comparing a single score.&lt;/p&gt;

&lt;p&gt;When assessing a tool, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Detection:&lt;/strong&gt; Can it find security defects that the author never mentioned?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context:&lt;/strong&gt; Does it compare the implementation with the stated requirements?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test quality:&lt;/strong&gt; Can it identify tests that validate insecure behavior?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visibility:&lt;/strong&gt; Are important findings easy for developers to discover?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enforcement:&lt;/strong&gt; Can critical findings influence the merge decision?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These dimensions determine whether review findings can translate into better engineering decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we're improving at Codzee
&lt;/h2&gt;

&lt;p&gt;Codzee scored 17/18 in this round, but the benchmark also showed where the product needs work.&lt;/p&gt;

&lt;p&gt;Our priorities are to improve critical-finding enforcement, make review comments more visible, and use pull request descriptions more effectively.&lt;/p&gt;

&lt;p&gt;We are sharing these gaps alongside the results because a useful engineering benchmark should reveal weaknesses, not just produce a leaderboard.&lt;/p&gt;

&lt;p&gt;You can learn more about Codzee at &lt;a href="https://www.codzee.io" rel="noopener noreferrer"&gt;https://www.codzee.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A question for developers and engineering teams:&lt;/strong&gt; When choosing an AI code review tool, which matters most to you: finding more defects, explaining them with better context, or ensuring critical vulnerabilities cannot pass through the merge workflow?&lt;/p&gt;

</description>
    </item>
    <item>
      <title>We Thought the Code Was Fine. Then the AI Found Something Else.</title>
      <dc:creator>Codzee.io</dc:creator>
      <pubDate>Fri, 09 Oct 2026 10:34:54 +0000</pubDate>
      <link>https://dev.to/codzee_io/we-thought-the-code-was-fine-then-the-ai-found-something-else-bk3</link>
      <guid>https://dev.to/codzee_io/we-thought-the-code-was-fine-then-the-ai-found-something-else-bk3</guid>
      <description>&lt;p&gt;We tested &lt;a href="//www.codzee.io"&gt;Codzee &lt;/a&gt;against Greptile, CodeRabbit, and CodeAnt. Here's what we learned from six rounds of AI code review benchmarks, including the bugs we found and the weaknesses we still need to fix.&lt;/p&gt;

&lt;h1&gt;
  
  
  We Thought the Code Was Fine. Then the AI Found Something Else.
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Sometimes, the most important bugs are the ones nobody thinks to look for.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We've all seen it happen.&lt;/p&gt;

&lt;p&gt;The code looks fine. The tests pass. The pull request gets approved. Then, later, someone discovers a bug that should have been caught during review.&lt;/p&gt;

&lt;p&gt;That's one of the problems we want Codzee to help solve.&lt;/p&gt;

&lt;p&gt;But as a team building an AI code review tool, we had to ask ourselves an uncomfortable question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is Codzee actually good at reviewing code, or does it just look good in a demo?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We decided to find out.&lt;/p&gt;

&lt;p&gt;We tested Codzee against Greptile, CodeRabbit, and CodeAnt using the same pull requests, deliberately planted bugs, and a predefined answer key.&lt;/p&gt;

&lt;p&gt;We ran six rounds, making each round harder than the last. Round six taught us the most — and it was the first round Codzee won.&lt;/p&gt;

&lt;p&gt;Here's what happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  The experiment: making the bugs harder to find
&lt;/h2&gt;

&lt;p&gt;For Round 6, we created a pull request involving a guest billing system. It handled invoices, payments, taxes, and cancellations.&lt;/p&gt;

&lt;p&gt;Instead of planting obvious mistakes in individual functions, we designed bugs that required understanding how different modules interacted.&lt;/p&gt;

&lt;p&gt;For example, one module could calculate an invoice correctly while another ignored it and recalculated the payment amount. Nothing crashed, but the customer could be charged a different amount from the one shown on the invoice.&lt;/p&gt;

&lt;p&gt;Here was our setup:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test detail&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pull request&lt;/td&gt;
&lt;td&gt;PR #7 in a test repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code size&lt;/td&gt;
&lt;td&gt;192 lines across two modules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Planted bugs&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-file bugs&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Domain&lt;/td&gt;
&lt;td&gt;Guest billing: invoices, payments, tax, cancellations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tools tested&lt;/td&gt;
&lt;td&gt;Codzee, Greptile, CodeRabbit, CodeAnt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scoring method&lt;/td&gt;
&lt;td&gt;Findings retrieved through the GitHub REST API and manually matched against a predefined answer key&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We wrote down the answer key before the review began. We also committed to publishing the weaknesses we found in Codzee.&lt;/p&gt;

&lt;h2&gt;
  
  
  The first surprise: the bugs hidden between files
&lt;/h2&gt;

&lt;p&gt;We started by examining the six defects that required understanding how two parts of the application worked together.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The bug&lt;/th&gt;
&lt;th&gt;Codzee&lt;/th&gt;
&lt;th&gt;Greptile&lt;/th&gt;
&lt;th&gt;CodeRabbit&lt;/th&gt;
&lt;th&gt;CodeAnt&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Payment ignores the invoice and recomputes the charge&lt;/td&gt;
&lt;td&gt;✓&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;tr&gt;
&lt;td&gt;Tax is never collected&lt;/td&gt;
&lt;td&gt;✓&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;tr&gt;
&lt;td&gt;Balance reads a key the invoice never writes&lt;/td&gt;
&lt;td&gt;✓&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;tr&gt;
&lt;td&gt;Due date compared against a datetime&lt;/td&gt;
&lt;td&gt;✓&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;tr&gt;
&lt;td&gt;A &lt;code&gt;timedelta&lt;/code&gt; passed where a day count is expected&lt;/td&gt;
&lt;td&gt;✓&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;tr&gt;
&lt;td&gt;Invoice numbers restart, causing payment records to collide&lt;/td&gt;
&lt;td&gt;✓&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;tr&gt;
&lt;td&gt;&lt;strong&gt;Total found&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6/6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6/6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5/6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4/6&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Four defects were caught by every tool. The two payment-related problems were more revealing: only Codzee and Greptile caught both.&lt;/p&gt;

&lt;p&gt;The most serious involved code that built an invoice and then ignored it, recalculating the charge from another module. This could cause a guest to be billed an amount that didn't match their invoice.&lt;/p&gt;

&lt;p&gt;Codzee marked this finding as Critical. Greptile also caught it, while CodeRabbit identified the tax issue but missed the related recomputation problem.&lt;/p&gt;

&lt;p&gt;That was precisely the kind of reasoning gap we wanted the experiment to expose.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Round 6 scoreboard
&lt;/h2&gt;

&lt;p&gt;Next, we looked at the complete results, including bugs outside the original answer key.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Evaluation metric&lt;/th&gt;
&lt;th&gt;Codzee&lt;/th&gt;
&lt;th&gt;Greptile&lt;/th&gt;
&lt;th&gt;CodeRabbit&lt;/th&gt;
&lt;th&gt;CodeAnt&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Bugs found (out of 13)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-file bugs (out of 6)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total findings&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Genuine bugs we didn't plant&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Findings on the test file&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;False positives&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Source: Codzee's internal Round 6 benchmark. Results represent one pull request, not a universal ranking of the tools.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Codzee found eight of the thirteen planted bugs. But the most interesting result wasn't the score itself.&lt;/p&gt;

&lt;p&gt;It found three additional genuine issues that weren't part of our original answer key.&lt;/p&gt;

&lt;p&gt;One involved a declined payment potentially being recorded as successful because the gateway receipt wasn't checked. Another involved a retry that could potentially charge a customer twice because the idempotency key changed.&lt;/p&gt;

&lt;p&gt;Greptile also found two additional issues, while CodeAnt found one.&lt;/p&gt;

&lt;p&gt;These findings reminded us that a predefined answer key is useful, but incomplete. Real applications can contain problems nobody thought to plant.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test file nobody reviewed
&lt;/h2&gt;

&lt;p&gt;We had also included three tests that asserted incorrect behavior.&lt;/p&gt;

&lt;p&gt;A passing test can create confidence in the code, but what if the test itself expects the wrong result?&lt;/p&gt;

&lt;p&gt;Codzee was the only tool to flag all three problematic tests in this round:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A due-date test should expect 15 June, not 1 July.&lt;/li&gt;
&lt;li&gt;An unpaid invoice should show 22,300 pence outstanding, not zero.&lt;/li&gt;
&lt;li&gt;An assertion simply repeated the implementation's calculation instead of independently checking the expected result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was a meaningful finding for us. Code review shouldn't stop at the implementation if the tests themselves are reinforcing incorrect behavior.&lt;/p&gt;

&lt;p&gt;It was also the fourth round out of six in which Codzee was the only tool to review the test file.&lt;/p&gt;

&lt;h2&gt;
  
  
  How did Codzee perform across all six rounds?
&lt;/h2&gt;

&lt;p&gt;One round can tell an interesting story, but repeated testing helps reveal patterns.&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;Round 1&lt;/th&gt;
&lt;th&gt;Round 2&lt;/th&gt;
&lt;th&gt;Round 3&lt;/th&gt;
&lt;th&gt;Round 4&lt;/th&gt;
&lt;th&gt;Round 5&lt;/th&gt;
&lt;th&gt;Round 6&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Greptile&lt;/td&gt;
&lt;td&gt;5/6&lt;/td&gt;
&lt;td&gt;10/12&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;6/6&lt;/td&gt;
&lt;td&gt;9/9&lt;/td&gt;
&lt;td&gt;7/13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codzee&lt;/td&gt;
&lt;td&gt;4/6&lt;/td&gt;
&lt;td&gt;9/12&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3/6&lt;/td&gt;
&lt;td&gt;8/9&lt;/td&gt;
&lt;td&gt;8/13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CodeRabbit&lt;/td&gt;
&lt;td&gt;4/6&lt;/td&gt;
&lt;td&gt;8/12&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;5/6&lt;/td&gt;
&lt;td&gt;8/9&lt;/td&gt;
&lt;td&gt;6/13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CodeAnt&lt;/td&gt;
&lt;td&gt;1/6&lt;/td&gt;
&lt;td&gt;8/12&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4/6&lt;/td&gt;
&lt;td&gt;6/9&lt;/td&gt;
&lt;td&gt;6/13&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;The rounds used different numbers of planted bugs. The Round 3 figures are reproduced as reported in the original article.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Our honest reading of these results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Greptile&lt;/strong&gt; was the most consistent across the series and found 13 unplanted bugs over six rounds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codzee&lt;/strong&gt; recorded its first win in Round 6 and found its first three unplanted issues in that round.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CodeRabbit&lt;/strong&gt; produced fewer findings and offered particularly clear fix guidance on the tax issue in Round 6.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CodeAnt&lt;/strong&gt; identified several defects, but struggled more with cross-file issues in this round.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are observations from our tests, not permanent rankings of the products.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we got wrong — and what we still need to fix
&lt;/h2&gt;

&lt;p&gt;We promised ourselves that we would publish the bad news, too.&lt;/p&gt;

&lt;p&gt;Three planted bugs defeated all four tools: a mutable default argument, a function-local import that obscured the recomputation path, and a rounding issue that could silently lose a penny on every invoice.&lt;/p&gt;

&lt;p&gt;We also found weaknesses in Codzee itself.&lt;/p&gt;

&lt;p&gt;Of its 18 findings, only 10 appeared inline on the code diff. The other eight were in the summary, making them harder to connect directly to the relevant code.&lt;/p&gt;

&lt;p&gt;Our inline placement rates across the six rounds were 50%, 100%, 0%, 75%, 25%, and 56%. That inconsistency is something we need to address.&lt;/p&gt;

&lt;p&gt;There was another problem: despite identifying a Critical payment defect, Codzee's merge-check conclusion remained neutral. A serious finding should be reflected clearly in the signal developers use when deciding whether to merge.&lt;/p&gt;

&lt;p&gt;The good news is that repeated testing also helped us identify improvements in severity calibration. In Round 6, Codzee assigned the Critical rating to the payment defect and graded the problematic tests as Major.&lt;/p&gt;

&lt;p&gt;But there is still work to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we learned
&lt;/h2&gt;

&lt;p&gt;This experiment reinforced a few lessons we think matter to anyone choosing an AI code review tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Context matters.&lt;/strong&gt; Bugs can hide in the relationship between modules, even when individual functions look reasonable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Unexpected findings matter.&lt;/strong&gt; A tool should be evaluated on legitimate issues beyond the bugs deliberately planted for testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. More comments don't necessarily mean better reviews.&lt;/strong&gt; Greptile found six of six cross-file bugs with nine findings. Codzee produced twice as many findings and found one additional planted bug.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Tests deserve scrutiny.&lt;/strong&gt; Three incorrect tests were present, and only Codzee flagged them in this round.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Placement is part of quality.&lt;/strong&gt; A finding is more useful when developers can see exactly where the problem occurs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. AI review doesn't replace linters.&lt;/strong&gt; All four tools missed defects that mechanical checks may be better equipped to catch.&lt;/p&gt;

&lt;p&gt;We also didn't test everything. Security depth, CI/CD integration, customization, scale, and team adoption were outside the scope of this experiment.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, should you trust Codzee?
&lt;/h2&gt;

&lt;p&gt;We don't think trust should come from a marketing claim — including ours.&lt;/p&gt;

&lt;p&gt;In Round 6, Codzee found eight of thirteen planted bugs, matched Greptile on all six cross-file defects, identified three additional genuine issues, and flagged three problematic tests.&lt;/p&gt;

&lt;p&gt;Those results encouraged us. The missed bugs, inconsistent inline placement, and neutral merge signal gave us clear areas to improve.&lt;/p&gt;

&lt;p&gt;Greptile remained a strong competitor, and every tool missed something.&lt;/p&gt;

&lt;p&gt;We build Codzee, so we're not neutral observers. That's exactly why we believe the methodology, results, and limitations should be visible.&lt;/p&gt;

&lt;p&gt;Our goal isn't to generate more comments. It's to help developers find meaningful problems before they reach production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you're evaluating AI code review tools, don't take our word for it. Test them on your own pull requests.&lt;/strong&gt; Hide a bug between two files, include a test that asserts the wrong behavior, and see what each tool catches.&lt;/p&gt;

&lt;p&gt;You can explore Codzee at &lt;a href="https://www.codzee.io" rel="noopener noreferrer"&gt;codzee.io&lt;/a&gt; and read the &lt;/p&gt;

&lt;p&gt;We'd love to hear from other developers: what's the kind of bug your AI reviewer consistently misses?&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Our client Tested 4 AI Code Review Tools on the Same PR and Here’s What They Missed</title>
      <dc:creator>Codzee.io</dc:creator>
      <pubDate>Wed, 07 Oct 2026 05:58:34 +0000</pubDate>
      <link>https://dev.to/codzee_io/our-client-tested-4-ai-code-review-tools-on-the-same-pr-and-heres-what-they-missed-4p5a</link>
      <guid>https://dev.to/codzee_io/our-client-tested-4-ai-code-review-tools-on-the-same-pr-and-heres-what-they-missed-4p5a</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu2xm0jt7lq4wnbim637i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu2xm0jt7lq4wnbim637i.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We wanted to answer a simple question:&lt;/p&gt;

&lt;p&gt;How well do today’s leading AI &lt;a href="https://www.codzee.io/" rel="noopener noreferrer"&gt;code review &lt;/a&gt;tools actually reason about bugs that cross module boundaries?&lt;/p&gt;

&lt;p&gt;So we gave &lt;a href="https://www.codzee.io/" rel="noopener noreferrer"&gt;Codzee&lt;/a&gt;, Greptile, CodeRabbit, and CodeAnt the exact same pull request.&lt;/p&gt;

&lt;p&gt;The PR contained 10 planted defects across 172 lines and 2 modules, with unit-related bugs involving kilometres, metres, miles, pence, pounds, seconds, minutes, and more.&lt;/p&gt;

&lt;p&gt;The result?&lt;/p&gt;

&lt;p&gt;Three tools tied at the top. But Codzee found something nobody else did.&lt;br&gt;
Press enter or click to view image in full size&lt;/p&gt;

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

&lt;p&gt;On paper, it looks like a three-way tie.&lt;/p&gt;

&lt;p&gt;But one finding changed the story.&lt;/p&gt;

&lt;p&gt;The Bug Nobody Had Found Before&lt;br&gt;
One of the planted defects involved banker’s rounding in a fare calculation.&lt;/p&gt;

&lt;p&gt;The code used Python’s round() behavior in a situation where commercial rounding was expected.&lt;/p&gt;

&lt;p&gt;For a fare of 1,250 pence, Python rounds it to:&lt;/p&gt;

&lt;p&gt;1,200 pence — not 1,300.&lt;/p&gt;

&lt;p&gt;Codzee caught it.&lt;/p&gt;

&lt;p&gt;But it didn’t stop at saying “rounding may be incorrect.”&lt;/p&gt;

&lt;p&gt;It reasoned through the behavior of Python’s standard library and explained the actual commercial consequence: half-pound fares can round differently depending on whether the pound value is even or odd.&lt;/p&gt;

&lt;p&gt;Greptile, CodeRabbit, and CodeAnt all reviewed the same function.&lt;/p&gt;

&lt;p&gt;None of them caught it.&lt;/p&gt;

&lt;p&gt;And this wasn’t just a one-off.&lt;/p&gt;

&lt;p&gt;This rounding defect had appeared in previous benchmark exercises. Across 16 benchmarked pull requests and roughly 500 findings, no tool had reported it.&lt;/p&gt;

&lt;p&gt;Until Codzee.&lt;/p&gt;

&lt;p&gt;That was the moment the benchmark became more interesting than a simple scorecard.&lt;/p&gt;

&lt;p&gt;The Full Defect Breakdown&lt;br&gt;
Press enter or click to view image in full size&lt;/p&gt;

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

&lt;p&gt;The benchmark isn’t about claiming perfection. It’s about understanding where each tool succeeds, where it fails, and why.&lt;/p&gt;

&lt;p&gt;What the Developer Running the Benchmark Noticed&lt;br&gt;
From the developer’s perspective, another result stood out.&lt;/p&gt;

&lt;p&gt;Codzee didn’t just review the implementation.&lt;/p&gt;

&lt;p&gt;It also looked at the tests.&lt;/p&gt;

&lt;p&gt;Two of its findings identified tests that were effectively validating incorrect behavior including a massively inflated fare and an unrealistic journey duration.&lt;/p&gt;

&lt;p&gt;That matters because a bad test can make a bad implementation look correct.&lt;/p&gt;

&lt;p&gt;A useful AI reviewer needs to question both.&lt;/p&gt;

&lt;p&gt;And Then We Found an Even Bigger Lesson&lt;br&gt;
The most interesting result wasn’t actually which tool won.&lt;/p&gt;

&lt;p&gt;It was the 90% detection rate for unit-related defects.&lt;/p&gt;

&lt;p&gt;These same types of defects had survived nine previous rounds across the tools.&lt;/p&gt;

&lt;p&gt;This time, both modules explicitly stated their units in their opening docstrings.&lt;/p&gt;

&lt;p&gt;That small change made a huge difference.&lt;/p&gt;

&lt;p&gt;The lesson?&lt;br&gt;
Give the reviewer context, and the reviewer can reason better.&lt;/p&gt;

&lt;p&gt;For developers, documenting units is almost free.&lt;/p&gt;

&lt;p&gt;For AI code review systems, it provides the context needed to connect values across functions and modules.&lt;/p&gt;

&lt;p&gt;So, What Did We Learn?&lt;br&gt;
Codzee&lt;br&gt;
9/10 — tied for first and the only tool to catch the banker’s-rounding defect.&lt;/p&gt;

&lt;p&gt;Greptile&lt;br&gt;
9/10 — strong reasoning and consistently concrete findings.&lt;/p&gt;

&lt;p&gt;CodeRabbit&lt;br&gt;
9/10 — particularly strong at connecting implementation issues with incorrect tests.&lt;/p&gt;

&lt;p&gt;CodeAnt&lt;br&gt;
7/10 — good critical-severity detection, but missed the most severe fare calculation defect.&lt;/p&gt;

&lt;p&gt;And Codzee?&lt;/p&gt;

&lt;p&gt;Two consecutive strong rounds.&lt;/p&gt;

&lt;p&gt;After winning Round 6 with 8/13, Codzee tied for first in Round 7 with 9/10.&lt;/p&gt;

&lt;p&gt;The Takeaway&lt;br&gt;
The benchmark started with a simple goal:&lt;/p&gt;

&lt;p&gt;Find the bugs.&lt;/p&gt;

&lt;p&gt;But the more interesting question became:&lt;/p&gt;

&lt;p&gt;Can an AI code reviewer actually understand why the code is wrong?&lt;/p&gt;

&lt;p&gt;The banker’s-rounding result suggests Codzee is getting closer to that level of reasoning.&lt;/p&gt;

&lt;p&gt;It understood the code → calculation → consequence.&lt;/p&gt;

&lt;p&gt;And that’s ultimately what we want from an AI code reviewer&lt;/p&gt;

&lt;p&gt;Not more comments. Better understanding.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>codereview</category>
      <category>developer</category>
      <category>programming</category>
    </item>
    <item>
      <title>We Tested 4 AI Code Review Tools on the Same PR and The Results Were Surprising…</title>
      <dc:creator>Codzee.io</dc:creator>
      <pubDate>Mon, 05 Oct 2026 10:53:21 +0000</pubDate>
      <link>https://dev.to/codzee_io/we-tested-4-ai-code-review-tools-on-the-same-pr-and-the-results-were-surprising-4l1i</link>
      <guid>https://dev.to/codzee_io/we-tested-4-ai-code-review-tools-on-the-same-pr-and-the-results-were-surprising-4l1i</guid>
      <description>&lt;p&gt;&lt;em&gt;A case study from six rounds of testing &lt;a href="https://www.codzee.io/" rel="noopener noreferrer"&gt;Codzee &lt;/a&gt;against Greptile, CodeRabbit and CodeAnt&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  It started with a question we couldn't answer
&lt;/h2&gt;

&lt;p&gt;We build an AI code review tool called &lt;strong&gt;Codzee&lt;/strong&gt;. At some point we realized we couldn't answer a simple question: &lt;em&gt;is it actually good?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Our demos looked great, and so did every competitor's. Every landing page promises "context-aware" and "low-noise" review. None of that tells you what happens when a tool meets a real pull request with a bug it can't find by pattern matching.&lt;/p&gt;

&lt;p&gt;So we stopped reading feature lists and ran an experiment. We built pull requests with bugs planted in them, gave &lt;strong&gt;the same PR to our tool and three competitors&lt;/strong&gt;, and checked what each one caught.&lt;/p&gt;

&lt;p&gt;We've now done this six times, and each round was harder than the last. This is the story of round six, the round that taught us the most, and the first one Codzee won.&lt;/p&gt;




&lt;h2&gt;
  
  
  The rules we set for ourselves
&lt;/h2&gt;

&lt;p&gt;Because we were testing our own product, we didn't trust ourselves. We set two rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The answer key comes first.&lt;/strong&gt; We wrote down every planted bug &lt;em&gt;before&lt;/em&gt; the pull request was opened, so nobody could redefine what counts as a "catch" afterward.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Publish the bad news too.&lt;/strong&gt; If our tool stumbled, it goes in the write-up.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's the setup for round six:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;#7 in a test repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;192 lines across two modules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Planted bugs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-file bugs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Domain&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Guest billing: invoices, payments, tax, cancellations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tools&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Codzee, Greptile, CodeRabbit, CodeAnt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;How we scored&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pulled every finding from the GitHub REST API and matched them to bugs by hand&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Qodo was excluded from this benchmark for billing reasons.&lt;/p&gt;




&lt;h2&gt;
  
  
  The trap we built
&lt;/h2&gt;

&lt;p&gt;Most AI review demos show a tool catching a bug in one function. That's easy mode, so we designed round six around something harder.&lt;/p&gt;

&lt;p&gt;The two modules were tied together by a written contract. It said things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The invoice total is &lt;strong&gt;pre-tax&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The balance reads a key called &lt;code&gt;amount_pence&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The due date is a &lt;strong&gt;date&lt;/strong&gt;, not a datetime.&lt;/li&gt;
&lt;li&gt;Invoice numbers are &lt;strong&gt;unique for life&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Six of our 13 bugs were &lt;strong&gt;cross-file defects&lt;/strong&gt;: each module looks correct on its own, and the error exists only in how they fit together. You can't find them by reading either file. You have to read both against the contract.&lt;/p&gt;

&lt;p&gt;The centerpiece was this one:&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="c1"&gt;# Illustrative sketch, not copied from the PR
&lt;/span&gt;&lt;span class="n"&gt;invoice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_invoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;booking&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# built...
&lt;/span&gt;&lt;span class="n"&gt;charge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;booking&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;# ...ignored, then recomputed
&lt;/span&gt;&lt;span class="n"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;guest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;charge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c1"&gt;# guest sees one number, pays another
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The payment path builds an invoice, throws it away, and recomputes the charge from another module. The guest is billed a figure that appears nowhere on their invoice, with tax and any extra lines dropped. Nothing crashes, and every function does its own job correctly.&lt;/p&gt;




&lt;h2&gt;
  
  
  The first thing we noticed: speed
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Codzee finished in 57 seconds.&lt;/strong&gt; CodeAnt took about two minutes. Greptile took 3 minutes 43 seconds. (We didn't record a time for CodeRabbit.)&lt;/p&gt;

&lt;p&gt;We wouldn't read much into this, because the timings may not be directly comparable. But it was the first visible difference, before anyone looked at the findings.&lt;/p&gt;




&lt;h2&gt;
  
  
  The second thing we noticed: nobody was wrong
&lt;/h2&gt;

&lt;p&gt;We pulled every finding from the API and matched them against the answer key. &lt;strong&gt;There were zero false positives across all four tools.&lt;/strong&gt; Every finding anyone posted was real, so in this round the differences weren't about who was wrong. They were about who saw more, and where they put what they saw.&lt;/p&gt;




&lt;h2&gt;
  
  
  The seam
&lt;/h2&gt;

&lt;p&gt;Here's how the four tools did on the six cross-file bugs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The bug&lt;/th&gt;
&lt;th&gt;Codzee&lt;/th&gt;
&lt;th&gt;Greptile&lt;/th&gt;
&lt;th&gt;CodeRabbit&lt;/th&gt;
&lt;th&gt;CodeAnt&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Payment ignores the invoice, recomputes the charge&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✓&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;tr&gt;
&lt;td&gt;&lt;strong&gt;Tax is never collected&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✓&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;tr&gt;
&lt;td&gt;Balance reads a key the invoice never writes&lt;/td&gt;
&lt;td&gt;✓&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;tr&gt;
&lt;td&gt;Due date compared against a datetime&lt;/td&gt;
&lt;td&gt;✓&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;tr&gt;
&lt;td&gt;A &lt;code&gt;timedelta&lt;/code&gt; passed where a day count is expected&lt;/td&gt;
&lt;td&gt;✓&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;tr&gt;
&lt;td&gt;Invoice numbers restart, payment records collide&lt;/td&gt;
&lt;td&gt;✓&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;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6/6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6/6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5/6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4/6&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Four of the six were caught by everyone. These were &lt;strong&gt;visible mismatches&lt;/strong&gt;, such as a wrong key, a wrong type or a counter that resets. A tool sees a value in one place and the wrong shape of it in another.&lt;/p&gt;

&lt;p&gt;The top two rows, the money bugs, were different. They split the field.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Only Codzee and Greptile found the recomputed charge&lt;/strong&gt;, and in our assessment Codzee's description was the more complete of the two. It was also the one bug Codzee marked &lt;strong&gt;Critical&lt;/strong&gt;, which felt right, since it's the only defect in the PR that silently bills a guest the wrong amount.&lt;/p&gt;

&lt;p&gt;The most interesting result belonged to &lt;strong&gt;CodeRabbit&lt;/strong&gt;. It commented on the tax problem in the &lt;em&gt;same function&lt;/em&gt; but never noticed that the function was also ignoring the invoice it had just built. It saw the symptom and not the cause sitting a few lines away. We can't see inside the tool, so we can't say why. It was exactly the gap we'd designed the round to expose.&lt;/p&gt;

&lt;p&gt;CodeRabbit did something else well, though. Its fix guidance on the tax bug was the clearest of the round: include tax in both the amount due and the gateway charge, with both call sites named.&lt;/p&gt;




&lt;h2&gt;
  
  
  The plot twist: the bugs we didn't plant
&lt;/h2&gt;

&lt;p&gt;This is the part of the round that changed how we think about our own tool.&lt;/p&gt;

&lt;p&gt;Our answer key is limited by our imagination. Real code has bugs nobody planted, so we also counted &lt;strong&gt;genuine findings outside the key&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For the first five rounds, this was the criticism we couldn't answer: &lt;strong&gt;Codzee had found zero of them. Greptile had found eleven.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In round six, Codzee found three. Two were serious, and &lt;em&gt;no other tool reported either one&lt;/em&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The declined card that counted as paid.&lt;/strong&gt; The payment path never checks the gateway receipt. A declined card is recorded as a full payment, so the invoice shows as settled and no money was taken.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The retry that charges twice.&lt;/strong&gt; After a timeout, a retry builds a new invoice with a new number. That changes the idempotency key, and the guest gets charged twice.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Both require following control flow across steps, not reading a single line.&lt;/p&gt;

&lt;p&gt;Greptile found two of its own, and both are about durability. An unsupported country code silently inherits the 20% GB rate, so an invoice looks plausible with a tax figure nobody chose. And payment records live only in an in-memory dictionary, so a restart wipes the history that balance and refund calculations rely on.&lt;/p&gt;

&lt;p&gt;CodeAnt found one, in keeping with its style, which was precise and focused on a single function. Its findings included an unsynchronized read-and-increment on the invoice counter, unvalidated payment amounts, and a getter that hands back the live internal list so callers can edit the ledger.&lt;/p&gt;




&lt;h2&gt;
  
  
  The test file nobody opened
&lt;/h2&gt;

&lt;p&gt;Three tests in the diff asserted the wrong behavior.&lt;/p&gt;

&lt;p&gt;That's a nasty class of bug, because a passing test makes a defect look intentional. A reviewer who sees green reasonably assumes the behavior is correct.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Only Codzee opened the test file.&lt;/strong&gt; This is now the fourth round out of six where it's been the only tool to do so. It posted one finding per bad test, each naming the correct expected value:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The due-date test should expect &lt;strong&gt;15 June&lt;/strong&gt;, not 1 July.&lt;/li&gt;
&lt;li&gt;An unpaid invoice should show &lt;strong&gt;22,300 pence outstanding&lt;/strong&gt;, not zero.&lt;/li&gt;
&lt;li&gt;Asserting that amount due equals the total just restates the implementation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Greptile, CodeRabbit and CodeAnt had nothing to say about the test file.&lt;/p&gt;




&lt;h2&gt;
  
  
  The scoreboard
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Codzee&lt;/th&gt;
&lt;th&gt;Greptile&lt;/th&gt;
&lt;th&gt;CodeRabbit&lt;/th&gt;
&lt;th&gt;CodeAnt&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bugs found (of 13)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-file (of 6)&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total findings&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real bugs we didn't plant&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Findings on test file&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;False positives&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Codzee found the most bugs, had a perfect cross-file score and was the only tool with test-file findings. But the margin over Greptile was &lt;strong&gt;one defect on one PR&lt;/strong&gt;. Greptile matched us on the hardest dimension with half as many findings.&lt;/p&gt;




&lt;h2&gt;
  
  
  What none of us caught
&lt;/h2&gt;

&lt;p&gt;Three bugs defeated all four tools:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A mutable default argument&lt;/strong&gt;, used in two functions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A function-local import&lt;/strong&gt; that hid the recompute from view&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A truncation&lt;/strong&gt; that quietly loses a penny on every invoice&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first is a textbook lint finding. In round one, nobody caught banker's rounding or an unreachable return either. As far as we can tell, none of these tools runs a linter, though we haven't verified how each works internally.&lt;/p&gt;

&lt;p&gt;We don't know why every tool missed these. Our hypothesis, and it is only a hypothesis, is that these tools put their effort into semantic reasoning and less into mechanical checks and subtle numeric behavior.&lt;/p&gt;

&lt;p&gt;The practical lesson: &lt;strong&gt;an AI reviewer doesn't replace your linter.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The part where our own tool let us down
&lt;/h2&gt;

&lt;p&gt;If we stopped here, this would be a flattering story, and we promised it wouldn't be.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem 1: almost half the findings weren't on the code.&lt;/strong&gt; Of Codzee's 18 findings, only &lt;strong&gt;10 were posted inline&lt;/strong&gt; on the diff. The other 8 sat in the summary, including all three test findings, the ones that most need to sit next to the code they describe. Across six rounds, our inline rate has been 50%, 100%, 0%, 75%, 25%, 56%, with no trend. Every other tool anchored its findings inline.&lt;/p&gt;

&lt;p&gt;Someone reading the pull request sees less of the win than the analysis shows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem 2: eighteen findings and no verdict.&lt;/strong&gt; Codzee flagged a Critical that bills guests incorrectly, and the check conclusion was still &lt;strong&gt;neutral&lt;/strong&gt;. A neutral check can't block a merge. Greptile found fewer bugs and still reported a confidence score.&lt;/p&gt;

&lt;p&gt;There was one thing we got right, though. In round five, Codzee had graded a worthless test suite as Minor, which was poor severity calibration. In round six it used three levels (&lt;strong&gt;1 Critical, 12 Major, 5 Minor&lt;/strong&gt;), the Critical went to the right bug, and the bad tests were graded Major. Running the same experiment repeatedly let us confirm that a problem we found actually got fixed.&lt;/p&gt;




&lt;h2&gt;
  
  
  The six-round story
&lt;/h2&gt;

&lt;p&gt;One round is an anecdote, so here's the full ramp:&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;R1&lt;/th&gt;
&lt;th&gt;R2&lt;/th&gt;
&lt;th&gt;R3&lt;/th&gt;
&lt;th&gt;R4&lt;/th&gt;
&lt;th&gt;R5&lt;/th&gt;
&lt;th&gt;R6&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Greptile&lt;/td&gt;
&lt;td&gt;5/6&lt;/td&gt;
&lt;td&gt;10/12&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;6/6&lt;/td&gt;
&lt;td&gt;9/9&lt;/td&gt;
&lt;td&gt;7/13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codzee&lt;/td&gt;
&lt;td&gt;4/6&lt;/td&gt;
&lt;td&gt;9/12&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3/6&lt;/td&gt;
&lt;td&gt;8/9&lt;/td&gt;
&lt;td&gt;8/13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CodeRabbit&lt;/td&gt;
&lt;td&gt;4/6&lt;/td&gt;
&lt;td&gt;8/12&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;5/6&lt;/td&gt;
&lt;td&gt;8/9&lt;/td&gt;
&lt;td&gt;6/13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CodeAnt&lt;/td&gt;
&lt;td&gt;1/6&lt;/td&gt;
&lt;td&gt;8/12&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4/6&lt;/td&gt;
&lt;td&gt;6/9&lt;/td&gt;
&lt;td&gt;6/13&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The honest reading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Greptile&lt;/strong&gt; is the most consistent tool in the series. It has never had a weak round and has found 13 unplanted bugs across six.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codzee&lt;/strong&gt; is rising. It got its first win, its first unplanted finds and a fixed severity problem, but it started behind.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CodeRabbit&lt;/strong&gt; is steady and precise, and consistently the lowest-volume tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CodeAnt&lt;/strong&gt; is strong wherever a defect is visible in one function and weaker wherever it requires comparing two things.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What we learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. The real test is reasoning distance.&lt;/strong&gt; Every tool can read a function. The ones that stand out can read a function &lt;em&gt;against a contract in another file&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The hardest bugs are absences.&lt;/strong&gt; An ignored invoice and a missing receipt check are things the code &lt;em&gt;didn't do&lt;/em&gt;, and they're harder to flag than a line that's visibly wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. More comments aren't a better review.&lt;/strong&gt; Greptile got six of six cross-file from nine findings. We posted twice as many and found one more bug.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Read the tests.&lt;/strong&gt; Three bad tests were quietly vouching for three bugs, and only one tool noticed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. The best findings are often unplanted.&lt;/strong&gt; The declined card and the double charge weren't in our key, and they may be the two most serious findings in the PR.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Placement is part of quality.&lt;/strong&gt; A finding that isn't next to the code is easy to miss.&lt;/p&gt;




&lt;h2&gt;
  
  
  Steal this checklist
&lt;/h2&gt;

&lt;p&gt;You can use it even if you never touch Codzee.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Plant a cross-file bug.&lt;/strong&gt; Have one module quietly ignore what another returns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plant a bad test.&lt;/strong&gt; See whether the tool opens the test file.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Count findings, then count what you'd act on.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check where comments land,&lt;/strong&gt; inline or buried in a summary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check severity.&lt;/strong&gt; Does "Critical" mean critical?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the merge signal.&lt;/strong&gt; Can it block a merge or only comment?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the fix guidance.&lt;/strong&gt; Does it name call sites and expected values?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Track unplanted finds.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep your linter.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test what we didn't:&lt;/strong&gt; security depth, CI/CD integration, customization, scale and team adoption were outside this experiment.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;This was an observational comparison, not a statistically significant benchmark.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It was &lt;strong&gt;one PR in this round&lt;/strong&gt;: 192 lines, one domain, and probably one language.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;We wrote the answer key.&lt;/strong&gt; Codzee's team designed the bugs and has a stake in the result.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Matching findings to bugs was done by hand,&lt;/strong&gt; which involves judgment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool versions and configurations&lt;/strong&gt; may differ across tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time figures&lt;/strong&gt; may not be directly comparable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tools change fast.&lt;/strong&gt; These results describe the day of the run.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing here shows that any tool is universally better.&lt;/p&gt;




&lt;h2&gt;
  
  
  What happens next
&lt;/h2&gt;

&lt;p&gt;Round seven pushes reasoning distance further. The same quantity will be &lt;strong&gt;metres in one module and kilometres in another&lt;/strong&gt;, the classic units-crossing-a-boundary bug. Every tool missed that class in an earlier benchmark.&lt;/p&gt;

&lt;p&gt;We expect it to be hard, and we'll publish the results either way.&lt;/p&gt;




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

&lt;p&gt;We started this series wanting to know whether our tool was any good. After six rounds, the answer is more useful than yes or no.&lt;/p&gt;

&lt;p&gt;It's good at some things, such as reading across files, reviewing tests and finding bugs nobody planted. It's still weak at others, namely inline placement and merge verdicts. And it's competing with tools that are better in places: Greptile on consistency, CodeRabbit on precision, CodeAnt on single-function hazards.&lt;/p&gt;

&lt;p&gt;If you're choosing an AI code review tool, don't trust a feature list, ours included. Hide a bug in the gap between two files, put one wrong test in the diff, and see who notices.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;We build Codzee and ran this benchmark ourselves. The methodology and limitations are above. We'd rather you check our work than trust it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What kinds of bugs do you find AI reviewers consistently miss? Tell us in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>softwaredevelopment</category>
      <category>testing</category>
    </item>
    <item>
      <title>what if Dismissed an AI Code Review Comment. Then I Checked the Next PR.</title>
      <dc:creator>Codzee.io</dc:creator>
      <pubDate>Mon, 21 Sep 2026 13:30:50 +0000</pubDate>
      <link>https://dev.to/codzee_io/what-if-dismissed-an-ai-code-review-comment-then-i-checked-the-next-pr-20j0</link>
      <guid>https://dev.to/codzee_io/what-if-dismissed-an-ai-code-review-comment-then-i-checked-the-next-pr-20j0</guid>
      <description>&lt;p&gt;I dismissed an AI code review comment.&lt;/p&gt;

&lt;p&gt;Nothing unusual there.&lt;/p&gt;

&lt;p&gt;What was interesting was what happened in the next pull request.&lt;/p&gt;

&lt;p&gt;A few &lt;a href="https://codzee.io/" rel="noopener noreferrer"&gt;PRs&lt;/a&gt; later, a similar pattern appeared in the code. I expected the same review comment.&lt;/p&gt;

&lt;p&gt;It wasn't there.&lt;/p&gt;

&lt;p&gt;That small behavior points to a bigger question for AI-assisted code review:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What should an AI reviewer do when an engineer disagrees with it?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the answer is simply “nothing,” the reviewer stays stateless.&lt;/p&gt;

&lt;p&gt;If the answer is “learn from the decision,” review behavior can become more aligned with the repository over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The annoying part of automated review
&lt;/h2&gt;

&lt;p&gt;AI code review can catch things that are easy to miss.&lt;/p&gt;

&lt;p&gt;But there is another failure mode: &lt;strong&gt;repetition&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A reviewer finds something that looks questionable according to a general coding guideline.&lt;/p&gt;

&lt;p&gt;The developer knows the pattern is intentional.&lt;/p&gt;

&lt;p&gt;The comment gets dismissed.&lt;/p&gt;

&lt;p&gt;Then another PR comes along.&lt;/p&gt;

&lt;p&gt;The same pattern appears.&lt;/p&gt;

&lt;p&gt;The reviewer raises the same concern.&lt;/p&gt;

&lt;p&gt;The developer dismisses it again.&lt;/p&gt;

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

&lt;p&gt;At some point, the problem isn't that the reviewer cannot find issues.&lt;/p&gt;

&lt;p&gt;The problem is that it hasn't learned which issues &lt;strong&gt;this repository doesn't need to discuss repeatedly&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That is where dismissal-learning becomes interesting.&lt;/p&gt;

&lt;h2&gt;
  
  
  PR #1: The first review
&lt;/h2&gt;

&lt;p&gt;Imagine a repository where a particular implementation pattern is intentional.&lt;/p&gt;

&lt;p&gt;An AI reviewer sees it and leaves a comment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI reviewer:

Consider changing X to Y for better consistency.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The comment is reasonable in isolation.&lt;/p&gt;

&lt;p&gt;But the repository has a reason for using X.&lt;/p&gt;

&lt;p&gt;So the engineer dismisses the comment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dismissed.

This pattern is intentional in this repository.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Normally, that interaction is over.&lt;/p&gt;

&lt;p&gt;The comment disappears from the PR.&lt;/p&gt;

&lt;p&gt;But the dismissal contains useful information.&lt;/p&gt;

&lt;h2&gt;
  
  
  A dismissal is engineering feedback
&lt;/h2&gt;

&lt;p&gt;A dismissal doesn't necessarily mean:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The reviewer is useless.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It can mean:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This recommendation doesn't apply to this codebase.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's an important distinction.&lt;/p&gt;

&lt;p&gt;Repositories have local conventions that aren't always obvious from the code itself.&lt;/p&gt;

&lt;p&gt;Some exist because of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;compatibility requirements&lt;/li&gt;
&lt;li&gt;architectural decisions&lt;/li&gt;
&lt;li&gt;performance constraints&lt;/li&gt;
&lt;li&gt;legacy integrations&lt;/li&gt;
&lt;li&gt;operational considerations&lt;/li&gt;
&lt;li&gt;team conventions&lt;/li&gt;
&lt;li&gt;deliberate trade-offs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A generic reviewer doesn't automatically know all of these.&lt;/p&gt;

&lt;p&gt;The developer does.&lt;/p&gt;

&lt;p&gt;So when a developer dismisses a review comment, that decision can be treated as feedback about the repository.&lt;/p&gt;

&lt;p&gt;The interesting part is what happens next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turning the dismissal into repository memory
&lt;/h2&gt;

&lt;p&gt;Instead of storing only the conversation, the system can extract the underlying pattern from the interaction.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review comment
      ↓
Developer dismissal
      ↓
Underlying pattern identified
      ↓
Repository memory
      ↓
Future reviews use that context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Repository memory:

"This pattern is accepted/preferred in this repository."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important word here is &lt;strong&gt;repository&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This isn't a global rule saying that the pattern is always correct.&lt;/p&gt;

&lt;p&gt;It is context associated with this particular codebase.&lt;/p&gt;

&lt;p&gt;That's important because engineering decisions are rarely universal.&lt;/p&gt;

&lt;p&gt;A pattern that is discouraged in one repository may be completely intentional in another.&lt;/p&gt;

&lt;h2&gt;
  
  
  PR #2: The same situation appears again
&lt;/h2&gt;

&lt;p&gt;Now a different pull request arrives.&lt;/p&gt;

&lt;p&gt;The code contains a similar pattern.&lt;/p&gt;

&lt;p&gt;Without repository memory, the reviewer might produce:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI reviewer:

Consider changing X to Y for better consistency.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which leads to another dismissal.&lt;/p&gt;

&lt;p&gt;With the previous feedback available as repository context, the reviewer can recognize that this type of comment has already been rejected for this repository.&lt;/p&gt;

&lt;p&gt;So the result is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI reviewer:

[No comment]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the part I find interesting.&lt;/p&gt;

&lt;p&gt;The system didn't need another configuration file.&lt;/p&gt;

&lt;p&gt;The developer didn't need to write a permanent rule manually.&lt;/p&gt;

&lt;p&gt;A previous engineering decision influenced a future review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before vs. after
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Without dismissal-learning&lt;/th&gt;
&lt;th&gt;With repository memory&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;First PR&lt;/td&gt;
&lt;td&gt;Review comment appears&lt;/td&gt;
&lt;td&gt;Review comment appears&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Developer response&lt;/td&gt;
&lt;td&gt;Dismisses comment&lt;/td&gt;
&lt;td&gt;Dismisses comment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feedback retained&lt;/td&gt;
&lt;td&gt;Little or no context&lt;/td&gt;
&lt;td&gt;Pattern becomes repository context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Similar later PR&lt;/td&gt;
&lt;td&gt;Same comment may return&lt;/td&gt;
&lt;td&gt;Reviewer can stay quiet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Developer experience&lt;/td&gt;
&lt;td&gt;Repeated explanation&lt;/td&gt;
&lt;td&gt;Less repeated feedback&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This doesn't mean every similar comment should disappear.&lt;/p&gt;

&lt;p&gt;Context still matters.&lt;/p&gt;

&lt;p&gt;But it creates the possibility of a reviewer that becomes better aligned with the repository's actual engineering decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  This isn't “remembering a conversation”
&lt;/h2&gt;

&lt;p&gt;This distinction is worth making.&lt;/p&gt;

&lt;p&gt;When people hear “AI memory,” they often think about conversational memory:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Remember what I told you yesterday.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's not the interesting part here.&lt;/p&gt;

&lt;p&gt;The useful unit is the &lt;strong&gt;engineering decision&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The system needs to understand something closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Pattern:
    X is used in this repository.

Developer decision:
    This usage is intentional.

Review implication:
    Don't repeatedly flag this pattern in the same context.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's repository memory.&lt;/p&gt;

&lt;p&gt;It turns feedback from an individual review interaction into context that can influence future reviews.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this feedback loop matters
&lt;/h2&gt;

&lt;p&gt;Code review isn't just about detecting problems.&lt;/p&gt;

&lt;p&gt;It's also about communicating what a team considers important.&lt;/p&gt;

&lt;p&gt;Human reviewers naturally build this context over time.&lt;/p&gt;

&lt;p&gt;After reviewing hundreds of pull requests, an experienced engineer knows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which patterns are normal&lt;/li&gt;
&lt;li&gt;which warnings are usually noise&lt;/li&gt;
&lt;li&gt;which conventions are intentional&lt;/li&gt;
&lt;li&gt;which architectural decisions shouldn't be revisited every PR&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An AI reviewer that doesn't retain any of that context has to rediscover the same information repeatedly.&lt;/p&gt;

&lt;p&gt;That creates review fatigue.&lt;/p&gt;

&lt;p&gt;A feedback loop changes the model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Find something
     ↓
Developer responds
     ↓
Learn from the response
     ↓
Apply the context later
     ↓
Produce more relevant feedback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal isn't fewer comments for the sake of fewer comments.&lt;/p&gt;

&lt;p&gt;It's &lt;strong&gt;higher-signal comments&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  There are caveats
&lt;/h2&gt;

&lt;p&gt;Dismissal-learning shouldn't turn every dismissal into a permanent rule.&lt;/p&gt;

&lt;p&gt;A developer can dismiss something because the comment is wrong.&lt;/p&gt;

&lt;p&gt;They can also dismiss it because the context is different in that particular file or PR.&lt;/p&gt;

&lt;p&gt;The architecture may change later.&lt;/p&gt;

&lt;p&gt;A pattern that was acceptable six months ago might become a technical debt issue today.&lt;/p&gt;

&lt;p&gt;So repository memory needs scope and context.&lt;/p&gt;

&lt;p&gt;The right behavior isn't:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This was dismissed once, never mention it again.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's closer to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Previous engineering feedback says this pattern is acceptable here. Consider that context before raising the same observation again.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a much more useful mental model.&lt;/p&gt;

&lt;h2&gt;
  
  
  The interesting shift
&lt;/h2&gt;

&lt;p&gt;AI code review is often discussed in terms of how much it can detect.&lt;/p&gt;

&lt;p&gt;I think another question deserves equal attention:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How well can it learn what not to say?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A reviewer that finds everything but repeatedly generates low-value comments can still create friction.&lt;/p&gt;

&lt;p&gt;A reviewer that incorporates developer feedback can gradually become more aligned with the repository.&lt;/p&gt;

&lt;p&gt;That's what makes this behavior interesting.&lt;/p&gt;

&lt;p&gt;One comment gets dismissed.&lt;/p&gt;

&lt;p&gt;The underlying pattern becomes repository memory.&lt;/p&gt;

&lt;p&gt;A similar situation appears later.&lt;/p&gt;

&lt;p&gt;The reviewer stays quiet.&lt;/p&gt;

&lt;p&gt;Not because the reviewer has learned everything.&lt;/p&gt;

&lt;p&gt;Not because repeated comments magically disappear.&lt;/p&gt;

&lt;p&gt;But because a real engineering decision from the repository has become part of the context for the next review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's what “gets quieter” actually means.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you want to see this behavior rather than read about it, the most useful test is simple: try it on a repository with an established review convention and watch what happens across two similar PRs.&lt;/p&gt;

&lt;p&gt;See the mechanism in action on your own repository.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Review AI-Generated Code Without Losing Your Mind</title>
      <dc:creator>Codzee.io</dc:creator>
      <pubDate>Thu, 17 Sep 2026 12:53:01 +0000</pubDate>
      <link>https://dev.to/codzee_io/how-to-review-ai-generated-code-without-losing-your-mind-5ah8</link>
      <guid>https://dev.to/codzee_io/how-to-review-ai-generated-code-without-losing-your-mind-5ah8</guid>
      <description>&lt;p&gt;&lt;a href="https://www.codzee.io/" rel="noopener noreferrer"&gt;AI &lt;/a&gt;can generate a working implementation in seconds.&lt;/p&gt;

&lt;p&gt;That doesn't mean the implementation is correct.&lt;/p&gt;

&lt;p&gt;For developers, this creates a slightly different code-review problem. You're no longer reviewing only code written from scratch by someone who understands the surrounding system. You're often reviewing code generated from a prompt, existing examples, partial context, or a combination of all three.&lt;/p&gt;

&lt;p&gt;The result can compile, pass tests, and look perfectly reasonable while still being wrong.&lt;/p&gt;

&lt;p&gt;The answer isn't to distrust every line of AI-generated code. It's to review it differently.&lt;/p&gt;

&lt;p&gt;This article presents a practical approach for reviewing AI-generated changes without spending an hour reading every line with equal attention.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Why AI-Generated Code Is Different
&lt;/h2&gt;

&lt;p&gt;AI-generated code has one particularly important characteristic:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It can be locally convincing while being globally incorrect.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Consider a simple API endpoint:&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="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/users/{user_id}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_db&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;user&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;HTTPException&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;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User not found&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;user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;But several questions immediately matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the endpoint authenticated?&lt;/li&gt;
&lt;li&gt;Can any authenticated user retrieve another user's data?&lt;/li&gt;
&lt;li&gt;Is &lt;code&gt;User&lt;/code&gt; safely serializable?&lt;/li&gt;
&lt;li&gt;Does returning the ORM object expose fields that shouldn't leave the API?&lt;/li&gt;
&lt;li&gt;Is there an authorization requirement that isn't represented in this function?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code itself doesn't contain enough information to answer those questions.&lt;/p&gt;

&lt;p&gt;That's common with AI-generated code.&lt;/p&gt;

&lt;p&gt;The model optimizes for producing a plausible implementation from the available context. It doesn't automatically understand your organization's security model, undocumented business rules, production traffic patterns, or architectural constraints.&lt;/p&gt;

&lt;p&gt;So code review needs to evaluate two things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does this code do?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What assumptions does this code make?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The second question is often where the important bugs are.&lt;/p&gt;




&lt;h1&gt;
  
  
  2. What Should Reviewers Check First?
&lt;/h1&gt;

&lt;p&gt;Don't start by reading every line.&lt;/p&gt;

&lt;p&gt;Start with the change itself.&lt;/p&gt;

&lt;p&gt;Before looking at implementation details, answer:&lt;/p&gt;

&lt;h3&gt;
  
  
  What problem is this PR solving?
&lt;/h3&gt;

&lt;p&gt;If the PR description says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Add caching to improve API performance."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;you should immediately ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What endpoint?&lt;/li&gt;
&lt;li&gt;What data is cached?&lt;/li&gt;
&lt;li&gt;How long?&lt;/li&gt;
&lt;li&gt;What invalidates it?&lt;/li&gt;
&lt;li&gt;Is stale data acceptable?&lt;/li&gt;
&lt;li&gt;Is the cache shared between users?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then look at the diff.&lt;/p&gt;

&lt;p&gt;A useful first pass is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Understand the intended behavior.&lt;/li&gt;
&lt;li&gt;Identify the files changed.&lt;/li&gt;
&lt;li&gt;Look for changes to security boundaries.&lt;/li&gt;
&lt;li&gt;Look for database and external-service interactions.&lt;/li&gt;
&lt;li&gt;Look for new state or caching.&lt;/li&gt;
&lt;li&gt;Check error-handling paths.&lt;/li&gt;
&lt;li&gt;Only then inspect implementation details.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This prevents a common review failure: spending ten minutes debating naming while missing a broken authorization check.&lt;/p&gt;




&lt;h1&gt;
  
  
  3. Logic and Edge Cases
&lt;/h1&gt;

&lt;p&gt;AI-generated code frequently handles the happy path well.&lt;/p&gt;

&lt;p&gt;The interesting bugs tend to live elsewhere.&lt;/p&gt;

&lt;p&gt;Suppose an AI-generated function processes payments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;calculateRefund&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestedAmount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestedAmount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;requestedAmount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks harmless.&lt;/p&gt;

&lt;p&gt;But what happens with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;calculateRefund&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function returns &lt;code&gt;-100&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What about floating-point currency?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What about an already-refunded order?&lt;/p&gt;

&lt;p&gt;What about concurrent refund requests?&lt;/p&gt;

&lt;p&gt;What about an order whose total has changed after partial refunds?&lt;/p&gt;

&lt;p&gt;The important question isn't:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Does this function work?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Under what conditions does this function stop working?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For AI-generated code, explicitly test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Empty input&lt;/li&gt;
&lt;li&gt;Null/undefined values&lt;/li&gt;
&lt;li&gt;Zero values&lt;/li&gt;
&lt;li&gt;Negative values&lt;/li&gt;
&lt;li&gt;Very large values&lt;/li&gt;
&lt;li&gt;Duplicate requests&lt;/li&gt;
&lt;li&gt;Concurrent requests&lt;/li&gt;
&lt;li&gt;Missing records&lt;/li&gt;
&lt;li&gt;Deleted records&lt;/li&gt;
&lt;li&gt;Partial failures&lt;/li&gt;
&lt;li&gt;Timeouts&lt;/li&gt;
&lt;li&gt;Retries&lt;/li&gt;
&lt;li&gt;Invalid state transitions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consider this common pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That looks reasonable until two requests arrive simultaneously.&lt;/p&gt;

&lt;p&gt;Both requests can observe that the user doesn't exist.&lt;/p&gt;

&lt;p&gt;Both attempt to create it.&lt;/p&gt;

&lt;p&gt;Now you've discovered a race condition.&lt;/p&gt;

&lt;p&gt;The database should usually enforce the invariant as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;users_email_unique&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the application needs to handle the constraint violation appropriately.&lt;/p&gt;

&lt;p&gt;Don't assume the generated code has considered concurrency simply because the sequential logic looks correct.&lt;/p&gt;




&lt;h1&gt;
  
  
  4. Security
&lt;/h1&gt;

&lt;p&gt;Security deserves an earlier pass than ordinary correctness.&lt;/p&gt;

&lt;p&gt;AI-generated code can accidentally introduce vulnerabilities because security depends heavily on context.&lt;/p&gt;

&lt;h3&gt;
  
  
  SQL injection
&lt;/h3&gt;

&lt;p&gt;Bad:&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;query&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;SELECT * FROM users WHERE email = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Better:&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;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE email = :email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&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;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But parameterized queries aren't the end of the review.&lt;/p&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is authorization enforced?&lt;/li&gt;
&lt;li&gt;Is sensitive information logged?&lt;/li&gt;
&lt;li&gt;Are secrets exposed?&lt;/li&gt;
&lt;li&gt;Is user-controlled input trusted?&lt;/li&gt;
&lt;li&gt;Are uploaded files validated?&lt;/li&gt;
&lt;li&gt;Are redirects controlled?&lt;/li&gt;
&lt;li&gt;Are permissions checked server-side?&lt;/li&gt;
&lt;li&gt;Are internal errors exposed to clients?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/admin/users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getUsers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;res&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="nx"&gt;users&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;Authentication exists.&lt;/p&gt;

&lt;p&gt;Authorization might not.&lt;/p&gt;

&lt;p&gt;A reviewer should ask whether &lt;code&gt;req.user&lt;/code&gt; actually has permission to access the endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  Logging is another common problem
&lt;/h3&gt;

&lt;p&gt;An AI-generated debugging statement might look innocent:&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;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Payment request: %s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the request contains card information, authentication tokens, addresses, or other sensitive fields, you've just created a data-exposure problem.&lt;/p&gt;

&lt;p&gt;Review security based on &lt;strong&gt;data flow&lt;/strong&gt;, not just individual functions.&lt;/p&gt;




&lt;h1&gt;
  
  
  5. Tests
&lt;/h1&gt;

&lt;p&gt;Passing tests don't prove that generated code is correct.&lt;/p&gt;

&lt;p&gt;They prove that the tested scenarios currently behave as expected.&lt;/p&gt;

&lt;p&gt;AI-generated tests can have another problem: they may simply encode the implementation rather than validate the requirement.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_discount&lt;/span&gt;&lt;span class="p"&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;calculate_discount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Useful, but incomplete.&lt;/p&gt;

&lt;p&gt;What about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;calculate_discount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;calculate_discount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;calculate_discount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;calculate_discount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;110&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;calculate_discount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More importantly, ask whether the tests represent actual business requirements.&lt;/p&gt;

&lt;p&gt;A strong review checks:&lt;/p&gt;

&lt;h3&gt;
  
  
  Coverage of behavior
&lt;/h3&gt;

&lt;p&gt;Not just line coverage.&lt;/p&gt;

&lt;p&gt;You want coverage of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Happy paths&lt;/li&gt;
&lt;li&gt;Failure paths&lt;/li&gt;
&lt;li&gt;Boundary conditions&lt;/li&gt;
&lt;li&gt;Authorization&lt;/li&gt;
&lt;li&gt;State transitions&lt;/li&gt;
&lt;li&gt;Retries&lt;/li&gt;
&lt;li&gt;Timeouts&lt;/li&gt;
&lt;li&gt;Concurrency where relevant&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Test independence
&lt;/h3&gt;

&lt;p&gt;Watch for tests that depend on execution order or shared mutable state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mock realism
&lt;/h3&gt;

&lt;p&gt;AI-generated tests often mock everything:&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;mock_db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fake_user&lt;/span&gt;
&lt;span class="n"&gt;mock_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&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;status&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;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test may pass while the real integration is broken.&lt;/p&gt;

&lt;p&gt;Mocks should isolate behavior intentionally, not hide the behavior you're supposed to verify.&lt;/p&gt;




&lt;h1&gt;
  
  
  6. Architecture
&lt;/h1&gt;

&lt;p&gt;This is where human review becomes particularly important.&lt;/p&gt;

&lt;p&gt;AI is very good at producing an implementation that fits the immediate request.&lt;/p&gt;

&lt;p&gt;It isn't necessarily good at knowing whether that implementation belongs in your architecture.&lt;/p&gt;

&lt;p&gt;Imagine a service that adds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;externalApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All inside one HTTP controller.&lt;/p&gt;

&lt;p&gt;Each operation may work.&lt;/p&gt;

&lt;p&gt;The architectural problem is that one request handler now owns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;persistence&lt;/li&gt;
&lt;li&gt;external integration&lt;/li&gt;
&lt;li&gt;caching&lt;/li&gt;
&lt;li&gt;notifications&lt;/li&gt;
&lt;li&gt;business logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The PR might technically work while making the system harder to evolve.&lt;/p&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does this belong in this layer?&lt;/li&gt;
&lt;li&gt;Are responsibilities separated?&lt;/li&gt;
&lt;li&gt;Is existing architecture being bypassed?&lt;/li&gt;
&lt;li&gt;Does this introduce a new abstraction unnecessarily?&lt;/li&gt;
&lt;li&gt;Does this duplicate existing functionality?&lt;/li&gt;
&lt;li&gt;Does this create a new dependency?&lt;/li&gt;
&lt;li&gt;Does the data flow still match the system's boundaries?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI-generated code often creates new helpers or abstractions when an existing one already exists.&lt;/p&gt;

&lt;p&gt;Search the repository before approving new infrastructure.&lt;/p&gt;

&lt;p&gt;The best implementation may already be somewhere in the codebase.&lt;/p&gt;




&lt;h1&gt;
  
  
  7. Maintainability
&lt;/h1&gt;

&lt;p&gt;Readable code isn't necessarily maintainable code.&lt;/p&gt;

&lt;p&gt;Look for unnecessary complexity.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shouldProcess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;disabled&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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="nx"&gt;deletedAt&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="nx"&gt;deletedAt&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;permissions&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;write&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code works, but the condition is difficult to reason about.&lt;/p&gt;

&lt;p&gt;Could the domain logic be clearer?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;canWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&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="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;disabled&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deletedAt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;write&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;false&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;During review, consider the next developer.&lt;/p&gt;

&lt;p&gt;Will they understand why this code exists?&lt;/p&gt;

&lt;p&gt;Can they modify it safely?&lt;/p&gt;

&lt;p&gt;Does it introduce duplicated logic?&lt;/p&gt;

&lt;p&gt;Does the naming describe the domain?&lt;/p&gt;

&lt;p&gt;Is there enough context around non-obvious decisions?&lt;/p&gt;

&lt;p&gt;AI can produce syntactically clean code very quickly. That doesn't automatically make the codebase easier to maintain.&lt;/p&gt;




&lt;h1&gt;
  
  
  8. Common AI-Review Mistakes
&lt;/h1&gt;

&lt;p&gt;There are several ways reviewers can make AI-generated PRs harder to review.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 1: Reviewing every line equally
&lt;/h3&gt;

&lt;p&gt;Not every line has the same risk.&lt;/p&gt;

&lt;p&gt;Focus attention on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Security boundaries&lt;/li&gt;
&lt;li&gt;Database mutations&lt;/li&gt;
&lt;li&gt;Authentication/authorization&lt;/li&gt;
&lt;li&gt;Concurrency&lt;/li&gt;
&lt;li&gt;External APIs&lt;/li&gt;
&lt;li&gt;State changes&lt;/li&gt;
&lt;li&gt;Error handling&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Mistake 2: Trusting green tests
&lt;/h3&gt;

&lt;p&gt;Tests are evidence.&lt;/p&gt;

&lt;p&gt;They're not proof.&lt;/p&gt;

&lt;p&gt;Ask what isn't tested.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 3: Focusing on style before behavior
&lt;/h3&gt;

&lt;p&gt;Don't spend ten comments discussing variable naming while missing incorrect business logic.&lt;/p&gt;

&lt;p&gt;Automate formatting and linting wherever possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 4: Assuming generated code understands your system
&lt;/h3&gt;

&lt;p&gt;It doesn't have your entire organizational context.&lt;/p&gt;

&lt;p&gt;Review assumptions explicitly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 5: Ignoring PR size
&lt;/h3&gt;

&lt;p&gt;A 1,500-line AI-generated PR is difficult to review regardless of how clean the code looks.&lt;/p&gt;

&lt;p&gt;Breaking changes into smaller PRs makes correctness easier to establish.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 6: Asking AI to review AI without verification
&lt;/h3&gt;

&lt;p&gt;An AI reviewer can be useful for finding patterns humans might miss.&lt;/p&gt;

&lt;p&gt;But its findings still require validation.&lt;/p&gt;

&lt;p&gt;Don't replace human judgment with another generated judgment.&lt;/p&gt;




&lt;h1&gt;
  
  
  9. A Practical AI-Code Review Checklist
&lt;/h1&gt;

&lt;p&gt;Before approving an AI-assisted PR, run through this checklist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Do I understand what problem this PR solves?&lt;/li&gt;
&lt;li&gt;[ ] Is the scope clear?&lt;/li&gt;
&lt;li&gt;[ ] Is the PR reasonably sized?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Logic
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Does the implementation match the requirement?&lt;/li&gt;
&lt;li&gt;[ ] What assumptions does the code make?&lt;/li&gt;
&lt;li&gt;[ ] What happens with empty, invalid, or extreme input?&lt;/li&gt;
&lt;li&gt;[ ] Are race conditions possible?&lt;/li&gt;
&lt;li&gt;[ ] Are retries and duplicate requests safe?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Security
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Is authentication correct?&lt;/li&gt;
&lt;li&gt;[ ] Is authorization enforced?&lt;/li&gt;
&lt;li&gt;[ ] Is user input handled safely?&lt;/li&gt;
&lt;li&gt;[ ] Are secrets protected?&lt;/li&gt;
&lt;li&gt;[ ] Could sensitive information reach logs?&lt;/li&gt;
&lt;li&gt;[ ] Are errors exposing internal information?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Data
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Are database queries safe?&lt;/li&gt;
&lt;li&gt;[ ] Are transactions required?&lt;/li&gt;
&lt;li&gt;[ ] Are constraints enforced at the database level?&lt;/li&gt;
&lt;li&gt;[ ] Could concurrent requests corrupt state?&lt;/li&gt;
&lt;li&gt;[ ] Is caching introducing stale or cross-user data?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tests
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Are important behaviors tested?&lt;/li&gt;
&lt;li&gt;[ ] Are edge cases covered?&lt;/li&gt;
&lt;li&gt;[ ] Are failure paths tested?&lt;/li&gt;
&lt;li&gt;[ ] Are tests testing requirements rather than implementation details?&lt;/li&gt;
&lt;li&gt;[ ] Are mocks hiding important integration behavior?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Does the change fit existing patterns?&lt;/li&gt;
&lt;li&gt;[ ] Is there already code that solves this problem?&lt;/li&gt;
&lt;li&gt;[ ] Are responsibilities in the right layer?&lt;/li&gt;
&lt;li&gt;[ ] Does the change introduce unnecessary dependencies?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Maintainability
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Is the code understandable?&lt;/li&gt;
&lt;li&gt;[ ] Is complexity justified?&lt;/li&gt;
&lt;li&gt;[ ] Are names meaningful?&lt;/li&gt;
&lt;li&gt;[ ] Is duplication introduced?&lt;/li&gt;
&lt;li&gt;[ ] Will another developer understand the reasoning six months from now?&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  The Goal Isn't to Review More. It's to Review Better.
&lt;/h1&gt;

&lt;p&gt;AI changes the economics of writing code.&lt;/p&gt;

&lt;p&gt;When producing another function, test, refactor, or API endpoint becomes dramatically cheaper, teams can generate changes faster than humans can carefully inspect them.&lt;/p&gt;

&lt;p&gt;That creates a new bottleneck:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;engineering attention.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The answer isn't to manually inspect every generated line forever.&lt;/p&gt;

&lt;p&gt;It's to make review more deliberate.&lt;/p&gt;

&lt;p&gt;Start with the highest-risk behavior.&lt;/p&gt;

&lt;p&gt;Question assumptions.&lt;/p&gt;

&lt;p&gt;Test boundaries.&lt;/p&gt;

&lt;p&gt;Trace data.&lt;/p&gt;

&lt;p&gt;Inspect security boundaries.&lt;/p&gt;

&lt;p&gt;Understand architectural consequences.&lt;/p&gt;

&lt;p&gt;And keep changes small enough that another engineer can actually understand them.&lt;/p&gt;

&lt;p&gt;AI can make code generation cheap.&lt;/p&gt;

&lt;p&gt;Human attention is still expensive.&lt;/p&gt;

&lt;p&gt;Code review should spend that attention where it has the highest chance of preventing something from reaching production.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>code</category>
      <category>developers</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>A Label Isn't a Code Review</title>
      <dc:creator>Codzee.io</dc:creator>
      <pubDate>Tue, 15 Sep 2026 10:53:47 +0000</pubDate>
      <link>https://dev.to/codzee_io/a-label-isnt-a-code-review-2lpg</link>
      <guid>https://dev.to/codzee_io/a-label-isnt-a-code-review-2lpg</guid>
      <description>&lt;p&gt;Consider two comments on the same piece of code.&lt;/p&gt;

&lt;p&gt;The first:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Consider refactoring this function for readability.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The second:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This returns &lt;code&gt;null&lt;/code&gt; when no active subscription exists, but the caller immediately dereferences &lt;code&gt;.plan&lt;/code&gt;. That turns a valid ‘no subscription’ state into an exception. Handle the empty case before accessing &lt;code&gt;plan&lt;/code&gt;.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first comment might be correct.&lt;/p&gt;

&lt;p&gt;The function might genuinely be difficult to read.&lt;/p&gt;

&lt;p&gt;But it leaves an important question unanswered:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the &lt;a href="https://codzee.io/" rel="noopener noreferrer"&gt;engineer supposed&lt;/a&gt; to do with that information?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The second comment contains considerably more information. It identifies a behavior, connects it to another part of the system, describes the failure mode, and suggests a direction.&lt;/p&gt;

&lt;p&gt;That seems straightforward.&lt;/p&gt;

&lt;p&gt;But it raises a more interesting question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What information does a code review comment actually need to contain to be useful?&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Code review isn't just defect detection
&lt;/h1&gt;

&lt;p&gt;It is tempting to think about code review as a simple pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;code
  ↓
reviewer
  ↓
problem detected
  ↓
comment
  ↓
code changed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But modern code review is doing several things simultaneously.&lt;/p&gt;

&lt;p&gt;It can catch defects.&lt;/p&gt;

&lt;p&gt;It can question design decisions.&lt;/p&gt;

&lt;p&gt;It can communicate project conventions.&lt;/p&gt;

&lt;p&gt;It can transfer knowledge between engineers.&lt;/p&gt;

&lt;p&gt;It can document decisions.&lt;/p&gt;

&lt;p&gt;It can expose assumptions that aren't visible in the code itself.&lt;/p&gt;

&lt;p&gt;Research on modern code review has found that reviewers and authors value activities beyond defect detection, including knowledge sharing and increasing awareness of the codebase.&lt;/p&gt;

&lt;p&gt;A Microsoft Research study of peer review also found that review is a form of knowledge exchange in which design rationale can be surfaced during the discussion.&lt;/p&gt;

&lt;p&gt;So a review comment isn't merely a defect marker.&lt;/p&gt;

&lt;p&gt;It is a small communication channel between two people who may have very different amounts of context.&lt;/p&gt;

&lt;p&gt;That changes what “good” means.&lt;/p&gt;




&lt;h1&gt;
  
  
  Flagging isn't explaining
&lt;/h1&gt;

&lt;p&gt;Consider:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Potential race condition.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There may indeed be a race condition.&lt;/p&gt;

&lt;p&gt;But the comment assumes the author will reconstruct the reviewer's reasoning.&lt;/p&gt;

&lt;p&gt;Where is the race?&lt;/p&gt;

&lt;p&gt;Under what interleaving?&lt;/p&gt;

&lt;p&gt;Does it produce incorrect data?&lt;/p&gt;

&lt;p&gt;Duplicate work?&lt;/p&gt;

&lt;p&gt;A crash?&lt;/p&gt;

&lt;p&gt;Is it harmless?&lt;/p&gt;

&lt;p&gt;Now compare:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Two requests can both observe that the key is absent before either calls &lt;code&gt;set()&lt;/code&gt;. That means &lt;code&gt;fetchValue()&lt;/code&gt; can execute twice and the second result can overwrite the first. If &lt;code&gt;fetchValue()&lt;/code&gt; has side effects, the current check-then-set sequence isn't sufficient; use an atomic get-or-create operation.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The second comment isn't merely more verbose.&lt;/p&gt;

&lt;p&gt;It exposes the reasoning.&lt;/p&gt;

&lt;p&gt;That distinction matters.&lt;/p&gt;

&lt;p&gt;A label such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[bug]
[performance]
[security]
[style]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What category did the reviewer assign this observation?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It doesn't necessarily answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why should I change the code?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A label is metadata.&lt;/p&gt;

&lt;p&gt;An explanation contains a model of the problem.&lt;/p&gt;




&lt;h1&gt;
  
  
  What information should a review comment contain?
&lt;/h1&gt;

&lt;p&gt;I don't think there is a universal template.&lt;/p&gt;

&lt;p&gt;But useful comments often contain some combination of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WHAT
What behavior or property is concerning?

WHY
Why does it matter?

CONTEXT
What assumption, constraint, or system behavior makes it important?

CONSEQUENCE
What can happen if it remains unchanged?

NEXT
What should the author investigate or change?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A comment doesn't need all five.&lt;/p&gt;

&lt;p&gt;For an obvious typo:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“&lt;code&gt;recieve&lt;/code&gt; → &lt;code&gt;receive&lt;/code&gt;.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;is probably sufficient.&lt;/p&gt;

&lt;p&gt;For a concurrency bug:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Two requests can both pass this check…”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;may need considerably more context.&lt;/p&gt;

&lt;p&gt;The amount of explanation should depend on the amount of ambiguity.&lt;/p&gt;




&lt;h1&gt;
  
  
  Correctness is not the same as usefulness
&lt;/h1&gt;

&lt;p&gt;This is an important distinction.&lt;/p&gt;

&lt;p&gt;Suppose a reviewer writes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This function is too complex.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And suppose the function really is too complex.&lt;/p&gt;

&lt;p&gt;The statement can be &lt;strong&gt;correct&lt;/strong&gt; while the review remains &lt;strong&gt;unhelpful&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The author still has to determine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which complexity?&lt;/li&gt;
&lt;li&gt;Why is it a problem?&lt;/li&gt;
&lt;li&gt;What boundary should change?&lt;/li&gt;
&lt;li&gt;Is the reviewer concerned about readability?&lt;/li&gt;
&lt;li&gt;Testing?&lt;/li&gt;
&lt;li&gt;Runtime behavior?&lt;/li&gt;
&lt;li&gt;Future modification?&lt;/li&gt;
&lt;li&gt;Something else?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compare:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This function now validates input, persists the record, and publishes an event. These operations have different failure semantics, so changing the event behavior now requires modifying persistence logic. Consider separating event publication from the persistence operation.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The second comment doesn't necessarily prescribe the correct refactoring.&lt;/p&gt;

&lt;p&gt;It does something more fundamental:&lt;/p&gt;

&lt;p&gt;It makes the reviewer's concern inspectable.&lt;/p&gt;

&lt;p&gt;The author can agree.&lt;/p&gt;

&lt;p&gt;The author can disagree.&lt;/p&gt;

&lt;p&gt;The author can explain why the coupling is intentional.&lt;/p&gt;

&lt;p&gt;That is a healthy property of a review comment.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why “why” matters
&lt;/h1&gt;

&lt;p&gt;Code frequently contains decisions that are not obvious from the code itself.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsDeleted&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ErrNotFound&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A reviewer might ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Why return not found instead of forbidden?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That question could be perfectly reasonable.&lt;/p&gt;

&lt;p&gt;But suppose the actual reason is security-sensitive API behavior:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The public endpoint intentionally returns &lt;code&gt;404&lt;/code&gt; for deleted users so callers can't distinguish between an existing deleted account and an account that never existed. This is consistent with the behavior of the other account lookup endpoints.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now the comment isn't just about an error code.&lt;/p&gt;

&lt;p&gt;It transfers context.&lt;/p&gt;

&lt;p&gt;This is why established review guidelines emphasize explaining why rather than simply describing what the code does. GitLab's review guidance explicitly recommends explaining rationale and making comments actionable.&lt;/p&gt;

&lt;p&gt;Google's code-review guidance similarly asks whether comments are clear and useful and distinguishes substantive concerns from optional style feedback.&lt;/p&gt;

&lt;p&gt;The “why” matters because code often cannot contain the entire reasoning behind itself.&lt;/p&gt;




&lt;h1&gt;
  
  
  But there is a danger in over-explaining
&lt;/h1&gt;

&lt;p&gt;Here is the obvious counterargument:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Could this framework cause reviewers to over-explain everything?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Absolutely.&lt;/p&gt;

&lt;p&gt;Imagine receiving this review comment:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This variable is named &lt;code&gt;i&lt;/code&gt;, which could potentially reduce semantic clarity because variable names are an important part of communicating intent. Consider using &lt;code&gt;index&lt;/code&gt; instead, since…”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nobody needs a paragraph for that.&lt;/p&gt;

&lt;p&gt;The problem is not lack of context.&lt;/p&gt;

&lt;p&gt;The problem is that the context is already obvious.&lt;/p&gt;

&lt;p&gt;A useful principle is therefore not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“More explanation is better.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“Enough explanation to remove meaningful ambiguity.”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For a typo, that's almost nothing.&lt;/p&gt;

&lt;p&gt;For a distributed-system failure mode, it might be several sentences.&lt;/p&gt;

&lt;p&gt;Comment length should follow uncertainty.&lt;/p&gt;




&lt;h1&gt;
  
  
  Signal versus noise
&lt;/h1&gt;

&lt;p&gt;This becomes especially important when review comments are cheap to generate.&lt;/p&gt;

&lt;p&gt;A human reviewer has a natural constraint: attention.&lt;/p&gt;

&lt;p&gt;An automated reviewer can potentially produce many observations.&lt;/p&gt;

&lt;p&gt;That creates a dangerous feedback loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;more detection
     ↓
more comments
     ↓
more things to inspect
     ↓
less attention per comment
     ↓
more review fatigue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem isn't hypothetical in the broader code-review literature.&lt;/p&gt;

&lt;p&gt;Researchers have studied reviewer participation, usefulness of comments, review size, reviewer experience, and other factors that affect modern code-review effectiveness.&lt;/p&gt;

&lt;p&gt;For example, Bosu, Greiler, and Bird analyzed approximately 1.5 million review comments from five Microsoft projects to investigate characteristics associated with useful review feedback. Their work found relationships between usefulness and factors including reviewer experience and change characteristics.&lt;/p&gt;

&lt;p&gt;The implication isn't that automated review is bad.&lt;/p&gt;

&lt;p&gt;It's that &lt;strong&gt;review bandwidth is finite&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If a system produces ten additional comments, the relevant question isn't whether all ten are technically defensible.&lt;/p&gt;

&lt;p&gt;It's whether the engineer's attention was spent better because they received them.&lt;/p&gt;




&lt;h1&gt;
  
  
  The preference problem
&lt;/h1&gt;

&lt;p&gt;There is another source of noise that has nothing to do with AI.&lt;/p&gt;

&lt;p&gt;Human reviewers do it constantly.&lt;/p&gt;

&lt;p&gt;Consider:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I'd use a map here.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That might be a good suggestion.&lt;/p&gt;

&lt;p&gt;But why?&lt;/p&gt;

&lt;p&gt;Maybe the map makes lookup complexity clearer.&lt;/p&gt;

&lt;p&gt;Maybe it avoids repeated scans.&lt;/p&gt;

&lt;p&gt;Maybe it is simply the reviewer's preferred style.&lt;/p&gt;

&lt;p&gt;Those are different claims.&lt;/p&gt;

&lt;p&gt;Compare:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I'd use a map here.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“We're doing a linear scan for every lookup, so the cost grows with the number of entries. Since this collection is accessed repeatedly, an indexed structure would avoid repeated scans.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now the author can evaluate the reasoning.&lt;/p&gt;

&lt;p&gt;This distinction between &lt;strong&gt;preference and engineering necessity&lt;/strong&gt; is important.&lt;/p&gt;

&lt;p&gt;Google's review guidance essentially argues that when multiple approaches are consistent with sound engineering principles, reviewers should accept the author's choice rather than treating their own preferred implementation as mandatory.&lt;/p&gt;

&lt;p&gt;A review culture that treats every preference as a defect creates noise even when every reviewer is acting in good faith.&lt;/p&gt;




&lt;h1&gt;
  
  
  Five examples
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1. Nullability
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Weak
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;“Handle null.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Stronger
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;“&lt;code&gt;getSubscription()&lt;/code&gt; returns &lt;code&gt;null&lt;/code&gt; when the user has no subscription, but this caller immediately reads &lt;code&gt;.plan&lt;/code&gt;. That turns a valid empty state into a runtime exception. Handle the empty case before dereferencing the result.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The second comment exposes the contract and failure mode.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Database queries
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Weak
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;“N+1.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Stronger
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;“&lt;code&gt;getCustomer()&lt;/code&gt; runs a database query inside this loop. With 500 orders, this can result in hundreds of additional queries. Load the required customers before the loop and reuse them here.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The label tells an experienced engineer what to investigate.&lt;/p&gt;

&lt;p&gt;The explanation tells them why this particular code has the problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Error semantics
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Weak
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;“Don't swallow the error.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Stronger
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;“Returning &lt;code&gt;nil&lt;/code&gt; here makes a failed request indistinguishable from a valid empty response. The caller treats &lt;code&gt;nil&lt;/code&gt; as success, so a network failure could be interpreted as ‘no data.’ Preserve the error so the caller can decide whether to retry or surface it.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The concern isn't merely “errors are important.”&lt;/p&gt;

&lt;p&gt;It's that information is being destroyed.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Authorization
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Weak
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;“Security issue.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Stronger
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;“Authentication establishes that the user has a valid session, but it doesn't establish that they can modify this document. Since the document isn't checked against the user's project, this path could allow cross-project modification. Verify authorization before the update.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The reviewer is identifying the missing security boundary.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Maintainability
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Weak
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;“This should be refactored.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Stronger
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;“This method now handles validation, persistence, retries, and notification. Those operations have different failure modes and are already changing independently. Separating the retry/notification path would keep future notification changes from touching persistence logic.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The second comment explains why the proposed structural change matters.&lt;/p&gt;




&lt;h1&gt;
  
  
  What if the reviewer doesn't know the author's intent?
&lt;/h1&gt;

&lt;p&gt;This is where the framework gets uncomfortable.&lt;/p&gt;

&lt;p&gt;Suppose a reviewer sees:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;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;404&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They might write:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This is wrong. A 404 should be an error.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But perhaps the API deliberately uses &lt;code&gt;404&lt;/code&gt; to represent “no matching resources.”&lt;/p&gt;

&lt;p&gt;The reviewer has made an assumption about intent.&lt;/p&gt;

&lt;p&gt;A more useful comment might be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Is &lt;code&gt;404&lt;/code&gt; expected to mean ‘no matching resources’ for this endpoint? If it represents a missing resource instead, returning an empty list here would hide an API error.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a question rather than a declaration.&lt;/p&gt;

&lt;p&gt;And sometimes that is the better review comment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good review doesn't require the reviewer to pretend they know things they don't know.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Uncertainty should be communicated as uncertainty.&lt;/p&gt;

&lt;p&gt;There is a major difference between:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This is wrong.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I may be missing context, but if X is possible, doesn't this create Y?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The second invites the author to supply missing information.&lt;/p&gt;




&lt;h1&gt;
  
  
  Review comments are conversations, not verdicts
&lt;/h1&gt;

&lt;p&gt;This is easy to forget when reviews are represented as annotations attached to lines.&lt;/p&gt;

&lt;p&gt;The interface makes a comment look like a judgment.&lt;/p&gt;

&lt;p&gt;But a review is usually a conversation.&lt;/p&gt;

&lt;p&gt;The reviewer proposes an interpretation.&lt;/p&gt;

&lt;p&gt;The author provides context.&lt;/p&gt;

&lt;p&gt;The two converge on a decision.&lt;/p&gt;

&lt;p&gt;That means a useful review comment doesn't necessarily need to prove that the reviewer is right.&lt;/p&gt;

&lt;p&gt;It needs to make the disagreement productive.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I think this could race if two workers execute this block concurrently. Is there a lock around this operation elsewhere that I'm missing?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That might be a better comment than a confidently incorrect:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This has a race condition. Fix it.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first comment contains uncertainty.&lt;/p&gt;

&lt;p&gt;The second hides it.&lt;/p&gt;




&lt;h1&gt;
  
  
  What about AI-generated review comments?
&lt;/h1&gt;

&lt;p&gt;AI makes this problem more interesting.&lt;/p&gt;

&lt;p&gt;An automated system can examine code at a scale that humans can't.&lt;/p&gt;

&lt;p&gt;It can notice patterns.&lt;/p&gt;

&lt;p&gt;It can compare implementations.&lt;/p&gt;

&lt;p&gt;It can identify suspicious data flows.&lt;/p&gt;

&lt;p&gt;It can explain some classes of issues.&lt;/p&gt;

&lt;p&gt;But there is a fundamental limitation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The code is not the entire system.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Intent may live in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;product requirements,&lt;/li&gt;
&lt;li&gt;architecture documents,&lt;/li&gt;
&lt;li&gt;incident history,&lt;/li&gt;
&lt;li&gt;database constraints,&lt;/li&gt;
&lt;li&gt;undocumented operational assumptions,&lt;/li&gt;
&lt;li&gt;conversations between engineers,&lt;/li&gt;
&lt;li&gt;downstream consumers,&lt;/li&gt;
&lt;li&gt;or simply someone's knowledge of why the system works the way it does.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An AI looking only at a pull request may not have access to that context.&lt;/p&gt;

&lt;p&gt;So consider an automated comment:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This validation is redundant.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Maybe it is.&lt;/p&gt;

&lt;p&gt;Or maybe the validation exists because an external system violates the contract once every few thousand requests.&lt;/p&gt;

&lt;p&gt;Without broader context, the model may correctly understand the local code and incorrectly understand the system.&lt;/p&gt;

&lt;p&gt;That's an important distinction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Local correctness is not necessarily system correctness.&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Can AI generate specific comments without understanding the whole system?
&lt;/h1&gt;

&lt;p&gt;Sometimes.&lt;/p&gt;

&lt;p&gt;For straightforward cases, specificity may not require deep system-level intent.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This value is checked for &lt;code&gt;None&lt;/code&gt; here, but dereferenced unconditionally three lines later.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The evidence is local.&lt;/p&gt;

&lt;p&gt;The model doesn't need to understand the entire architecture.&lt;/p&gt;

&lt;p&gt;But consider:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This retry count is too low.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That might require knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;upstream service behavior,&lt;/li&gt;
&lt;li&gt;timeout characteristics,&lt;/li&gt;
&lt;li&gt;SLA requirements,&lt;/li&gt;
&lt;li&gt;idempotency guarantees,&lt;/li&gt;
&lt;li&gt;traffic patterns,&lt;/li&gt;
&lt;li&gt;and production failure modes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The more a comment depends on external intent, the less safe it is to infer that intent from code alone.&lt;/p&gt;

&lt;p&gt;This suggests a useful boundary for automated review:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The system should distinguish observations supported directly by code from conclusions that depend on missing context.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That could mean expressing uncertainty.&lt;/p&gt;

&lt;p&gt;It could mean asking a question.&lt;/p&gt;

&lt;p&gt;It could mean requesting additional repository context.&lt;/p&gt;

&lt;p&gt;It could mean not commenting at all.&lt;/p&gt;

&lt;p&gt;Silence is sometimes better than a plausible but unsupported explanation.&lt;/p&gt;




&lt;h1&gt;
  
  
  Explanation isn't evidence
&lt;/h1&gt;

&lt;p&gt;There's another subtle failure mode.&lt;/p&gt;

&lt;p&gt;A detailed comment can sound authoritative simply because it is detailed.&lt;/p&gt;

&lt;p&gt;Consider an AI-generated comment:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This implementation creates a race condition because the cache is not synchronized across worker threads, which could cause stale reads and inconsistent state…”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It sounds convincing.&lt;/p&gt;

&lt;p&gt;But does the system actually know that the workers share the same cache?&lt;/p&gt;

&lt;p&gt;Does it know the operation is concurrent?&lt;/p&gt;

&lt;p&gt;Does it know stale reads matter?&lt;/p&gt;

&lt;p&gt;A long explanation can create &lt;strong&gt;false confidence&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So review quality cannot simply become:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“More detailed comments are better.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The real requirement is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The explanation should be proportional to the evidence.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A concise, well-supported observation can be more trustworthy than a sophisticated explanation built on assumptions.&lt;/p&gt;




&lt;h1&gt;
  
  
  The unit of review isn't always the line
&lt;/h1&gt;

&lt;p&gt;Another limitation of line-level comments is that many engineering problems aren't located on one line.&lt;/p&gt;

&lt;p&gt;Consider an authorization bug.&lt;/p&gt;

&lt;p&gt;The problem might be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;controller
    ↓
service
    ↓
repository
    ↓
database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The individual lines can all look reasonable.&lt;/p&gt;

&lt;p&gt;The vulnerability emerges from how the components interact.&lt;/p&gt;

&lt;p&gt;Likewise with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;distributed systems,&lt;/li&gt;
&lt;li&gt;caching,&lt;/li&gt;
&lt;li&gt;transactions,&lt;/li&gt;
&lt;li&gt;retries,&lt;/li&gt;
&lt;li&gt;consistency,&lt;/li&gt;
&lt;li&gt;API contracts,&lt;/li&gt;
&lt;li&gt;concurrency,&lt;/li&gt;
&lt;li&gt;lifecycle management.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sometimes the most useful review comment needs to refer to a &lt;strong&gt;relationship&lt;/strong&gt;, not a line.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The transaction ends before the event is published, so a successful response can be returned even if event publication fails. Is that intentional? If the event is part of the operation's contract, the transaction/outbox boundary probably needs to move.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's difficult to express as a simple label.&lt;/p&gt;

&lt;p&gt;It requires a model of the interaction between components.&lt;/p&gt;




&lt;h1&gt;
  
  
  So what makes a review comment good?
&lt;/h1&gt;

&lt;p&gt;I don't think the answer is “make every comment more detailed.”&lt;/p&gt;

&lt;p&gt;Nor is it “always explain the why.”&lt;/p&gt;

&lt;p&gt;There are cases where the why is obvious.&lt;/p&gt;

&lt;p&gt;Nor is it “never use labels.”&lt;/p&gt;

&lt;p&gt;Labels are useful.&lt;/p&gt;

&lt;p&gt;The more interesting answer is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A good review comment contains the minimum information necessary for another engineer to correctly evaluate the concern.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometimes that's:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Typo: &lt;code&gt;recieve&lt;/code&gt; → &lt;code&gt;receive&lt;/code&gt;.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometimes it's:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This returns &lt;code&gt;null&lt;/code&gt; when X happens, but Y dereferences it immediately, turning a valid state into an exception.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometimes it's a question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Is this 404 intentional? If not, are we hiding an API failure by returning an empty list?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometimes the right review is no comment at all.&lt;/p&gt;

&lt;p&gt;That last case matters.&lt;/p&gt;

&lt;p&gt;A reviewer should not be rewarded simply for finding something to say.&lt;/p&gt;




&lt;h1&gt;
  
  
  A small framework: WHAT → WHY → NEXT
&lt;/h1&gt;

&lt;p&gt;If I had to give reviewers a practical heuristic, it would be:&lt;/p&gt;

&lt;h3&gt;
  
  
  WHAT?
&lt;/h3&gt;

&lt;p&gt;What exactly are you observing?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This can return &lt;code&gt;null&lt;/code&gt;.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  WHY?
&lt;/h3&gt;

&lt;p&gt;Why does that matter?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The caller dereferences it immediately.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  NEXT?
&lt;/h3&gt;

&lt;p&gt;What should the author investigate or change?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Handle the empty case before accessing the property.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Together:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This can return &lt;code&gt;null&lt;/code&gt;, but the caller immediately dereferences it, which can turn a valid empty state into a runtime exception. Handle the empty case before accessing the property.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But the framework should be treated as a diagnostic tool, not a writing template.&lt;/p&gt;

&lt;p&gt;If the issue is obvious, don't force an explanation.&lt;/p&gt;

&lt;p&gt;If the reviewer is uncertain, don't manufacture certainty.&lt;/p&gt;

&lt;p&gt;If the concern depends on system context the reviewer doesn't have, ask.&lt;/p&gt;

&lt;p&gt;If the implementation is simply a matter of preference, say so.&lt;/p&gt;




&lt;h1&gt;
  
  
  The deeper problem
&lt;/h1&gt;

&lt;p&gt;There is a tendency to think of software engineering communication as secondary to the “real” engineering work.&lt;/p&gt;

&lt;p&gt;The code is the artifact.&lt;/p&gt;

&lt;p&gt;The review comment is just discussion around it.&lt;/p&gt;

&lt;p&gt;I'm not convinced that's true.&lt;/p&gt;

&lt;p&gt;A codebase contains decisions.&lt;/p&gt;

&lt;p&gt;Reviews contain the reasoning behind some of those decisions.&lt;/p&gt;

&lt;p&gt;When that reasoning is missing, future engineers have to reconstruct it.&lt;/p&gt;

&lt;p&gt;When it is vague, authors have to interpret it.&lt;/p&gt;

&lt;p&gt;When it is excessively noisy, people stop paying attention.&lt;/p&gt;

&lt;p&gt;And when it is confidently wrong, the review system can actively make engineering decisions worse.&lt;/p&gt;

&lt;p&gt;That applies equally to human and automated reviewers.&lt;/p&gt;

&lt;p&gt;The hard problem isn't merely detecting something suspicious.&lt;/p&gt;

&lt;p&gt;It's knowing &lt;strong&gt;what information another engineer needs to evaluate the observation&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Open questions
&lt;/h1&gt;

&lt;p&gt;I don't think we've solved this.&lt;/p&gt;

&lt;p&gt;A few questions seem particularly interesting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How much context is enough?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Can review systems estimate when a comment needs one sentence versus five?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can usefulness be measured without reducing it to “did the author change the code”?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A correct review may result in no change because the author provides missing context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How should automated reviewers represent uncertainty?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Should a system say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This is a bug.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;or:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“If X is possible, this may produce Y. Is X guaranteed not to happen?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;How much repository context should an AI reviewer consume before making a claim?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;More context can improve reasoning, but it can also increase cost and introduce irrelevant information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can automated systems distinguish preference from correctness reliably?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Or will they reproduce the same subjective review behavior humans already struggle with?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens to review quality when the cost of generating comments approaches zero?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If comments become effectively free, attention becomes the scarce resource.&lt;/p&gt;

&lt;p&gt;And perhaps the most basic question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Should the objective of code review be to find more problems, or to help engineers make better decisions?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those are related goals.&lt;/p&gt;

&lt;p&gt;They aren't necessarily the same.&lt;/p&gt;

&lt;p&gt;A label tells us that someone thinks there is a problem.&lt;/p&gt;

&lt;p&gt;A useful review comment gives us enough reasoning to decide whether they are right.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's why a label isn't a code review.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>code</category>
      <category>coding</category>
      <category>softwaredevelopment</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Why AI Code Review Needs to Learn From Developers</title>
      <dc:creator>Codzee.io</dc:creator>
      <pubDate>Tue, 08 Sep 2026 11:30:32 +0000</pubDate>
      <link>https://dev.to/codzee_io/why-ai-code-review-needs-to-learn-from-developers-bad</link>
      <guid>https://dev.to/codzee_io/why-ai-code-review-needs-to-learn-from-developers-bad</guid>
      <description>&lt;p&gt;After 15+ years of building software, &lt;a href="https://codzee.io/" rel="noopener noreferrer"&gt;reviewing pull requests&lt;/a&gt;, debugging production systems, and working with engineering teams, I've become increasingly convinced that we're asking the wrong question about AI code review.&lt;/p&gt;

&lt;p&gt;The obvious question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How accurately can AI detect problems in code?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's an important question.&lt;/p&gt;

&lt;p&gt;But there's another question that becomes more important once a team uses the system for months:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How well does the system understand what this particular team considers worth reviewing?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those are very different problems.&lt;/p&gt;

&lt;p&gt;A reviewer can be technically accurate and still be frustrating to use.&lt;/p&gt;

&lt;p&gt;It can identify real complexity, real duplication, real security risks, and real maintainability concerns—and still lose the trust of the engineers receiving its comments.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because software engineering isn't just a collection of rules.&lt;/p&gt;

&lt;p&gt;It's context, conventions, trade-offs, history, architecture, and judgment.&lt;/p&gt;

&lt;p&gt;And much of that knowledge isn't written anywhere.&lt;/p&gt;

&lt;p&gt;It's learned through interaction.&lt;/p&gt;

&lt;p&gt;That is why I believe the next generation of AI code review needs to learn from developers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Static Analysis Was Never the Whole Story
&lt;/h2&gt;

&lt;p&gt;We've had static analysis for decades.&lt;/p&gt;

&lt;p&gt;Linters are incredibly useful.&lt;/p&gt;

&lt;p&gt;They catch formatting problems, suspicious constructs, unused variables, dangerous patterns, complexity issues, and countless other classes of problems.&lt;/p&gt;

&lt;p&gt;Static analysis is deterministic and predictable.&lt;/p&gt;

&lt;p&gt;That's exactly why engineers like it.&lt;/p&gt;

&lt;p&gt;You can define a rule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if condition X is true
    report finding Y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then configure how that finding behaves.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Warning&lt;/li&gt;
&lt;li&gt;Error&lt;/li&gt;
&lt;li&gt;Info&lt;/li&gt;
&lt;li&gt;Ignore&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can add thresholds.&lt;/p&gt;

&lt;p&gt;You can exclude directories.&lt;/p&gt;

&lt;p&gt;You can configure allowlists and denylists.&lt;/p&gt;

&lt;p&gt;You can override rules for specific repositories.&lt;/p&gt;

&lt;p&gt;This works remarkably well for problems that can be expressed clearly.&lt;/p&gt;

&lt;p&gt;But there's a fundamental limitation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The configuration describes what we think matters. It doesn't necessarily describe what the team actually considers useful.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Those aren't always the same thing.&lt;/p&gt;




&lt;h1&gt;
  
  
  Configuration Has a Ceiling
&lt;/h1&gt;

&lt;p&gt;Consider a team working on a large backend system.&lt;/p&gt;

&lt;p&gt;They configure a complexity threshold:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;complexity &amp;gt; 10 → warning
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seems reasonable.&lt;/p&gt;

&lt;p&gt;Then the system starts flagging a particular class of functions.&lt;/p&gt;

&lt;p&gt;The team repeatedly dismisses them.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Maybe those functions are generated around a complex business domain.&lt;/p&gt;

&lt;p&gt;Maybe they're deliberately structured that way because splitting them would make the code harder to understand.&lt;/p&gt;

&lt;p&gt;Maybe the threshold is appropriate for most of the repository but not this subsystem.&lt;/p&gt;

&lt;p&gt;Maybe the team has accepted the complexity because the alternative introduces a worse architectural trade-off.&lt;/p&gt;

&lt;p&gt;You can solve this with another configuration rule.&lt;/p&gt;

&lt;p&gt;Add an exception.&lt;/p&gt;

&lt;p&gt;Then another exception.&lt;/p&gt;

&lt;p&gt;Then another.&lt;/p&gt;

&lt;p&gt;Eventually, your configuration starts looking like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flag X
unless repository Y
unless directory Z
unless pattern A
unless service B
unless annotation C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At some point, we're essentially trying to encode years of engineering judgment into a configuration file.&lt;/p&gt;

&lt;p&gt;That's not necessarily the best abstraction.&lt;/p&gt;




&lt;h1&gt;
  
  
  Engineering Teams Are Dynamic Systems
&lt;/h1&gt;

&lt;p&gt;The bigger problem is that repositories don't stay still.&lt;/p&gt;

&lt;p&gt;Neither do teams.&lt;/p&gt;

&lt;p&gt;An organization might move from a monolith to services.&lt;/p&gt;

&lt;p&gt;A team might adopt a new framework.&lt;/p&gt;

&lt;p&gt;A legacy subsystem might become effectively frozen.&lt;/p&gt;

&lt;p&gt;A new architectural standard might be introduced.&lt;/p&gt;

&lt;p&gt;Performance requirements might change.&lt;/p&gt;

&lt;p&gt;Security requirements might become stricter.&lt;/p&gt;

&lt;p&gt;The team might deliberately relax a particular style rule because it's slowing development without providing meaningful value.&lt;/p&gt;

&lt;p&gt;Configuration tends to represent a snapshot.&lt;/p&gt;

&lt;p&gt;Engineering practice is a moving target.&lt;/p&gt;

&lt;p&gt;That's why I think AI code review needs something beyond configuration.&lt;/p&gt;

&lt;p&gt;It needs a &lt;strong&gt;feedback loop&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Most Interesting Signal Might Be a Dismissal
&lt;/h1&gt;

&lt;p&gt;Here's a simple example.&lt;/p&gt;

&lt;p&gt;Suppose an AI review system repeatedly reports:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This function is too complex.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The developer dismisses the comment.&lt;/p&gt;

&lt;p&gt;The same pattern appears in another pull request.&lt;/p&gt;

&lt;p&gt;The developer dismisses it again.&lt;/p&gt;

&lt;p&gt;Then again.&lt;/p&gt;

&lt;p&gt;What should happen?&lt;/p&gt;

&lt;p&gt;A conventional system records three dismissed findings.&lt;/p&gt;

&lt;p&gt;End of story.&lt;/p&gt;

&lt;p&gt;But I think we're throwing away valuable information.&lt;/p&gt;

&lt;p&gt;Repeated dismissal could mean several things.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The rule is too broad
&lt;/h3&gt;

&lt;p&gt;Perhaps the complexity threshold isn't appropriate for this codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The system lacks context
&lt;/h3&gt;

&lt;p&gt;The function may look complex locally but be perfectly reasonable given the surrounding architecture.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The team has an intentional exception
&lt;/h3&gt;

&lt;p&gt;The engineers may understand the trade-off and have consciously decided to accept it.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The feedback is technically correct but practically irrelevant
&lt;/h3&gt;

&lt;p&gt;Something can be objectively improvable without being worth interrupting a developer about.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. The team may actually be wrong
&lt;/h3&gt;

&lt;p&gt;This possibility is important too.&lt;/p&gt;

&lt;p&gt;Learning from developers shouldn't mean blindly accepting every dismissal.&lt;/p&gt;

&lt;p&gt;Sometimes an engineer dismisses a legitimate security issue.&lt;/p&gt;

&lt;p&gt;Sometimes a team has normalized technical debt.&lt;/p&gt;

&lt;p&gt;Sometimes people ignore warnings because fixing them is inconvenient.&lt;/p&gt;

&lt;p&gt;So the answer isn't:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Developer dismissed it, therefore never report it again."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The better question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What does this interaction tell us, and how confident should we be when adapting?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a much more interesting engineering problem.&lt;/p&gt;




&lt;h1&gt;
  
  
  A Review Comment Is an Interaction
&lt;/h1&gt;

&lt;p&gt;We usually model code review as something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Code → Analysis → Finding
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But real code review is closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Code
  ↓
Analysis
  ↓
Finding
  ↓
Developer judgment
  ↓
Accept / dismiss / modify / ignore
  ↓
Future behavior
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That final step is where a lot of today's systems stop paying attention.&lt;/p&gt;

&lt;p&gt;But it may be the most valuable part.&lt;/p&gt;

&lt;p&gt;Every interaction provides information.&lt;/p&gt;

&lt;p&gt;A developer accepts a finding.&lt;/p&gt;

&lt;p&gt;That's a signal.&lt;/p&gt;

&lt;p&gt;A developer dismisses it.&lt;/p&gt;

&lt;p&gt;That's a signal.&lt;/p&gt;

&lt;p&gt;A developer repeatedly modifies recommendations in the same way.&lt;/p&gt;

&lt;p&gt;That's a signal.&lt;/p&gt;

&lt;p&gt;An entire team consistently ignores a category of feedback.&lt;/p&gt;

&lt;p&gt;That's a much stronger signal.&lt;/p&gt;

&lt;p&gt;The challenge is turning those signals into useful adaptation without creating unpredictable behavior.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Repository Shouldn't Be Treated as an Isolated Installation
&lt;/h1&gt;

&lt;p&gt;One thing that bothers me about the current mental model is the idea that every repository starts from zero.&lt;/p&gt;

&lt;p&gt;Install the tool.&lt;/p&gt;

&lt;p&gt;Configure it.&lt;/p&gt;

&lt;p&gt;Run it.&lt;/p&gt;

&lt;p&gt;Done.&lt;/p&gt;

&lt;p&gt;But an established codebase already contains enormous amounts of engineering knowledge.&lt;/p&gt;

&lt;p&gt;You can find it in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Existing implementation patterns&lt;/li&gt;
&lt;li&gt;Pull request discussions&lt;/li&gt;
&lt;li&gt;Architectural decisions&lt;/li&gt;
&lt;li&gt;Test strategies&lt;/li&gt;
&lt;li&gt;Naming conventions&lt;/li&gt;
&lt;li&gt;Error-handling approaches&lt;/li&gt;
&lt;li&gt;Accepted technical debt&lt;/li&gt;
&lt;li&gt;Framework-specific patterns&lt;/li&gt;
&lt;li&gt;Exceptions to general standards&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of this can be explicitly documented.&lt;/p&gt;

&lt;p&gt;Much of it isn't.&lt;/p&gt;

&lt;p&gt;An AI system operating inside that repository should have the opportunity to understand those patterns.&lt;/p&gt;

&lt;p&gt;Not simply analyze the current pull request in isolation.&lt;/p&gt;




&lt;h1&gt;
  
  
  False Positives Are More Expensive Than They Look
&lt;/h1&gt;

&lt;p&gt;False positives aren't just an accuracy problem.&lt;/p&gt;

&lt;p&gt;They're a trust problem.&lt;/p&gt;

&lt;p&gt;Imagine a developer receives ten review comments.&lt;/p&gt;

&lt;p&gt;Two are genuinely useful.&lt;/p&gt;

&lt;p&gt;Eight aren't.&lt;/p&gt;

&lt;p&gt;After a while, the developer stops carefully evaluating each comment.&lt;/p&gt;

&lt;p&gt;They start scanning.&lt;/p&gt;

&lt;p&gt;Eventually they start dismissing automatically.&lt;/p&gt;

&lt;p&gt;That's when the system has failed even if its underlying detection model is technically sophisticated.&lt;/p&gt;

&lt;p&gt;The cost isn't just the time spent reading irrelevant comments.&lt;/p&gt;

&lt;p&gt;The larger cost is &lt;strong&gt;signal degradation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once developers stop believing that a comment is likely to matter, even good findings become harder to trust.&lt;/p&gt;

&lt;p&gt;This is why reducing noise isn't merely a UX improvement.&lt;/p&gt;

&lt;p&gt;It is part of the intelligence of the system.&lt;/p&gt;




&lt;h1&gt;
  
  
  But Adaptation Needs Guardrails
&lt;/h1&gt;

&lt;p&gt;There's an obvious danger here.&lt;/p&gt;

&lt;p&gt;If we allow AI systems to learn from developer behavior without constraints, they could learn the wrong lessons.&lt;/p&gt;

&lt;p&gt;Imagine a team consistently dismisses security warnings because fixing them is inconvenient.&lt;/p&gt;

&lt;p&gt;Should the system become quieter?&lt;/p&gt;

&lt;p&gt;Probably not.&lt;/p&gt;

&lt;p&gt;Some categories should have stronger guarantees.&lt;/p&gt;

&lt;p&gt;Some findings should remain visible regardless of preference.&lt;/p&gt;

&lt;p&gt;Some adaptations should require explicit confirmation.&lt;/p&gt;

&lt;p&gt;Some changes should be explainable.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"This finding has been repeatedly dismissed for this repository. Should future findings of this type be deprioritized?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's very different from silently changing behavior.&lt;/p&gt;

&lt;p&gt;The goal shouldn't be autonomous customization at any cost.&lt;/p&gt;

&lt;p&gt;The goal should be &lt;strong&gt;safe adaptation&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  What Would an Adaptive Review System Look Like?
&lt;/h1&gt;

&lt;p&gt;I don't think this requires some magical autonomous agent.&lt;/p&gt;

&lt;p&gt;The underlying architecture could be relatively straightforward.&lt;/p&gt;

&lt;p&gt;Imagine maintaining a feedback model for findings.&lt;/p&gt;

&lt;p&gt;Something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Finding
├── category
├── location
├── repository context
├── confidence
├── severity
├── historical interactions
└── developer/team feedback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then review decisions become part of the system's future reasoning.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Finding A
→ accepted 27 times
→ rarely dismissed
→ high relevance

Finding B
→ dismissed 18 times
→ accepted 1 time
→ low relevance in this repository

Finding C
→ dismissed frequently by one team
→ accepted frequently by another
→ highly context dependent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the system isn't simply asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Is this technically a problem?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It can also ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How relevant has this type of finding historically been in this environment?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is a fundamentally different signal.&lt;/p&gt;

&lt;p&gt;And it can become increasingly repository-specific without requiring engineers to manually encode every preference.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Goal Isn't Fewer Findings
&lt;/h1&gt;

&lt;p&gt;This distinction matters.&lt;/p&gt;

&lt;p&gt;Adaptive code review shouldn't simply become a system that says less.&lt;/p&gt;

&lt;p&gt;The objective is &lt;strong&gt;higher-value output&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There will be situations where the correct behavior is to become more aggressive.&lt;/p&gt;

&lt;p&gt;Suppose a team repeatedly accepts findings related to a particular security pattern.&lt;/p&gt;

&lt;p&gt;The system now has evidence that this category matters.&lt;/p&gt;

&lt;p&gt;That should increase confidence that similar findings are worth surfacing.&lt;/p&gt;

&lt;p&gt;Adaptation should work in both directions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer behavior
       ↓
Observed preference
       ↓
Updated relevance
       ↓
Future review behavior
       ↓
More developer feedback
       ↓
Updated preference
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a feedback loop.&lt;/p&gt;

&lt;p&gt;And feedback loops are how systems become better aligned with their environment.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Metric We Should Care About
&lt;/h1&gt;

&lt;p&gt;This leads to a question I think the industry should spend more time discussing.&lt;/p&gt;

&lt;p&gt;We measure code-review systems by things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Detection accuracy&lt;/li&gt;
&lt;li&gt;Precision&lt;/li&gt;
&lt;li&gt;Recall&lt;/li&gt;
&lt;li&gt;False-positive rate&lt;/li&gt;
&lt;li&gt;Number of findings&lt;/li&gt;
&lt;li&gt;Vulnerability coverage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These metrics are useful.&lt;/p&gt;

&lt;p&gt;But they're incomplete.&lt;/p&gt;

&lt;p&gt;I'd also want to know:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What percentage of findings does a team consider useful?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And perhaps more importantly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does that percentage improve over time?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine two systems.&lt;/p&gt;

&lt;p&gt;System A finds 100 issues in the first month and continues finding roughly 100 every month.&lt;/p&gt;

&lt;p&gt;System B finds 70 issues initially, learns from developer interactions, and eventually surfaces 30 highly relevant findings.&lt;/p&gt;

&lt;p&gt;Which one is better?&lt;/p&gt;

&lt;p&gt;The answer depends on what those findings contain.&lt;/p&gt;

&lt;p&gt;But for an engineering team, I would strongly prefer the system that earns the right to speak less—and gets better at knowing when to speak.&lt;/p&gt;




&lt;h1&gt;
  
  
  This Is Bigger Than AI Code Review
&lt;/h1&gt;

&lt;p&gt;There's a broader principle here.&lt;/p&gt;

&lt;p&gt;Good developer tools don't merely perform tasks.&lt;/p&gt;

&lt;p&gt;They fit into human workflows.&lt;/p&gt;

&lt;p&gt;Git became useful because developers could build mental models around it.&lt;/p&gt;

&lt;p&gt;IDEs became useful because developers shaped them around their workflows.&lt;/p&gt;

&lt;p&gt;Linters became useful because teams could define what mattered.&lt;/p&gt;

&lt;p&gt;The next generation of AI tools has another opportunity:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;They can learn from the workflow itself.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's different from traditional configuration.&lt;/p&gt;

&lt;p&gt;Configuration says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Here is what we told the system."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Behavioral feedback says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Here is what the team actually does."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The combination could be much more powerful.&lt;/p&gt;




&lt;h1&gt;
  
  
  Signal Isn't a Feature. It's a Relationship.
&lt;/h1&gt;

&lt;p&gt;After 15+ years in software engineering, I don't think developer trust comes from a feature checklist.&lt;/p&gt;

&lt;p&gt;It comes from repeated evidence.&lt;/p&gt;

&lt;p&gt;You make a judgment.&lt;/p&gt;

&lt;p&gt;The system observes it.&lt;/p&gt;

&lt;p&gt;It adapts.&lt;/p&gt;

&lt;p&gt;You make another judgment.&lt;/p&gt;

&lt;p&gt;It adapts again.&lt;/p&gt;

&lt;p&gt;Over time, the system becomes less generic and more aligned with the environment in which it operates.&lt;/p&gt;

&lt;p&gt;That's what makes the idea of adaptive AI code review compelling to me.&lt;/p&gt;

&lt;p&gt;The next generation won't compete only on who can detect the most bugs.&lt;/p&gt;

&lt;p&gt;They'll compete on who can understand &lt;strong&gt;which bugs, patterns, and risks are actually worth surfacing to a particular engineering team.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because detection gets you into the workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Relevance keeps you there.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And relevance comes from understanding context.&lt;/p&gt;

&lt;p&gt;That is why I believe:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Signal isn't a feature. It's a relationship.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The best AI code-review systems will be the ones that earn that relationship carefully—through feedback, context, transparency, and repeated interaction.&lt;/p&gt;

&lt;p&gt;Not during installation.&lt;/p&gt;

&lt;p&gt;Over time.&lt;/p&gt;

&lt;p&gt;And ultimately, the real measure of an AI reviewer won't be how much it can say.&lt;/p&gt;

&lt;p&gt;It will be how quickly—and how safely—it earns the team's trust.&lt;br&gt;
:::&lt;/p&gt;

</description>
      <category>ai</category>
      <category>developers</category>
      <category>software</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>How can AI code review tools be evaluated reliably for real-world pull requests?</title>
      <dc:creator>Codzee.io</dc:creator>
      <pubDate>Tue, 01 Sep 2026 14:40:12 +0000</pubDate>
      <link>https://dev.to/codzee_io/how-can-ai-code-review-tools-be-evaluated-reliably-for-real-world-pull-requests-1pl3</link>
      <guid>https://dev.to/codzee_io/how-can-ai-code-review-tools-be-evaluated-reliably-for-real-world-pull-requests-1pl3</guid>
      <description>&lt;p&gt;I am evaluating &lt;a href="https://www.codzee.io/" rel="noopener noreferrer"&gt;AI-assisted code review systems&lt;/a&gt; and have found that published benchmarks use substantially different methodologies. Some replay historical bugs, some evaluate complete pull requests, some inject synthetic defects into real code, and others use human or LLM-based judgments of review comments.&lt;/p&gt;

&lt;p&gt;I am trying to design a fair evaluation for systems that review real-world pull requests.&lt;/p&gt;

&lt;p&gt;The main issues I am running into are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dataset construction:&lt;/strong&gt; Should the test set consist primarily of historical production bugs, clean pull requests, synthetic defects, or a combination?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ground truth:&lt;/strong&gt; How should a benchmark define the complete set of valid findings when the original human review may have missed defects?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Precision and recall:&lt;/strong&gt; Should findings be matched individually, or should a PR count as correct if the system identifies at least one valid issue?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;False positives:&lt;/strong&gt; How should speculative, stylistic, duplicate, or technically correct but non-actionable comments affect the score?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Severity:&lt;/strong&gt; Should critical security or correctness defects receive more weight than low-severity maintainability issues?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human review:&lt;/strong&gt; What level of expert annotation or adjudication is necessary to establish reliable ground truth?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repository context:&lt;/strong&gt; Should systems receive the full repository, issue description, tests, commit history, and search capabilities, or should all systems be restricted to the diff?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Languages and projects:&lt;/strong&gt; How should results be normalized across programming languages, repositories, domains, and different patch sizes?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reproducibility:&lt;/strong&gt; What artifacts and experimental details should be published so another team can independently reproduce the evaluation?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, a system that produces 20 comments and correctly identifies 10 defects is quite different from one that produces 5 comments and correctly identifies 4, even if their aggregate scores happen to be similar. Likewise, a benchmark based only on human review comments may penalize a system for finding a legitimate defect that the original reviewers missed.&lt;/p&gt;

&lt;p&gt;I am looking for a methodology that measures &lt;strong&gt;useful defect detection on real pull requests&lt;/strong&gt;, rather than simply similarity to historical review comments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What benchmark design and scoring procedure would provide the most statistically and methodologically defensible comparison of AI code review systems, including dataset construction, ground-truth validation, finding-level precision/recall, severity weighting, repository context, human adjudication, and reproducibility?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>codequality</category>
      <category>softwareengineering</category>
      <category>testing</category>
    </item>
    <item>
      <title>Your CODEOWNERS File Might Be Correct And Still Send Your PR to the Wrong Person</title>
      <dc:creator>Codzee.io</dc:creator>
      <pubDate>Mon, 31 Aug 2026 11:15:03 +0000</pubDate>
      <link>https://dev.to/codzee_io/your-codeowners-file-might-be-correct-and-still-send-your-pr-to-the-wrong-person-i8j</link>
      <guid>https://dev.to/codzee_io/your-codeowners-file-might-be-correct-and-still-send-your-pr-to-the-wrong-person-i8j</guid>
      <description>&lt;p&gt;You open a &lt;a href="https://www.codzee.io/" rel="noopener noreferrer"&gt;PR&lt;/a&gt;. GitHub looks at the changed paths, checks them against &lt;code&gt;CODEOWNERS&lt;/code&gt;, and auto-assigns a reviewer. This happens so often you probably don't think about it anymore. It's infrastructure — it just works.&lt;/p&gt;

&lt;p&gt;Except sometimes it doesn't, and it fails in a way that never throws an error.&lt;/p&gt;

&lt;p&gt;Here's a realistic example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight codeowners"&gt;&lt;code&gt;&lt;span class="c1"&gt;# CODEOWNERS&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;/payments/&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nf"&gt;@engineer-a&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Engineer A hasn't touched &lt;code&gt;payments/&lt;/code&gt; in eight months — they moved to a different team in Q1. Engineer B has made the last 18 commits in that directory and has reviewed most of the recent PRs that touched it. Engineer B isn't in the file.&lt;/p&gt;

&lt;p&gt;Your PR touches &lt;code&gt;payments/refunds.go&lt;/code&gt;. It gets routed to Engineer A.&lt;/p&gt;

&lt;p&gt;Nothing is misconfigured. The glob pattern matches. The username resolves to a real, active employee. GitHub does exactly what it's supposed to do. The routing logic is just missing context it was never designed to have.&lt;/p&gt;

&lt;p&gt;This is the gap this article is about: &lt;strong&gt;CODEOWNERS tells you who owns a file. It doesn't tell you who should review a specific change.&lt;/strong&gt; Those turn out to be different questions, and the difference gets bigger as your team and codebase grow.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. What CODEOWNERS Gets Right
&lt;/h2&gt;

&lt;p&gt;Before picking it apart, it's worth being clear that CODEOWNERS solves a real problem well, and nothing below is an argument for ripping it out.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Explicit ownership.&lt;/strong&gt; Anyone can look at one file and see who's accountable for a given path. No tribal knowledge required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Required reviews.&lt;/strong&gt; You can enforce that certain paths always get eyes from the right team before merge — critical for things like auth, billing, or infra-as-code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clear accountability.&lt;/strong&gt; When something in &lt;code&gt;/payments/&lt;/code&gt; breaks, there's no ambiguity about who's supposed to be paying attention to that code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple, predictable behavior.&lt;/strong&gt; It's a text file with glob patterns. No ML model, no black box, no surprises. You can &lt;code&gt;git blame&lt;/code&gt; it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a small team, or for stable, slow-moving parts of a codebase, this is close to sufficient. The problem shows up specifically as team size and code churn increase — which is exactly when the cost of getting routing wrong also goes up.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Where Static Routing Breaks
&lt;/h2&gt;

&lt;p&gt;CODEOWNERS answers one question — &lt;em&gt;who owns this path&lt;/em&gt; — and organizations often treat that as a proxy for a different question: &lt;em&gt;who should review this change&lt;/em&gt;. Here's where those diverge:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;Static CODEOWNERS&lt;/th&gt;
&lt;th&gt;Dynamic routing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;File ownership&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recent contribution&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Current expertise&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Review history&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reviewer load&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Expertise decay&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Walking through the "No" column:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Recent contribution&lt;/strong&gt; — CODEOWNERS has no time dimension. An entry from two years ago carries the same weight as one from yesterday.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Current expertise&lt;/strong&gt; — "Limited" because ownership is often a reasonable proxy for expertise &lt;em&gt;at the moment the file was written&lt;/em&gt;, but it's frozen after that. It doesn't update when someone's knowledge of an area grows or fades.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review history&lt;/strong&gt; — the file doesn't know who's actually been reviewing changes in an area, only who's nominally responsible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviewer load&lt;/strong&gt; — &lt;a href="https://www.codzee.io/will" rel="noopener noreferrer"&gt;https://www.codzee.io/will&lt;/a&gt; happily assign a fifth simultaneous PR to someone already buried in review requests. It has no concept of "currently busy."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expertise decay&lt;/strong&gt; — there's no mechanism for an owner's relevance to degrade over time as they stop working in an area, and no mechanism for someone else's relevance to rise as they start.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is a criticism of CODEOWNERS as a tool — it's just outside its scope. It's a static ownership file, not a reviewer-relevance model. The mistake is using it as if it were the second thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Model Expertise as Something That Changes
&lt;/h2&gt;

&lt;p&gt;The core idea: &lt;strong&gt;expertise isn't binary, and it isn't permanent.&lt;/strong&gt; "Alice owns payments" is a snapshot. What you actually want to know is closer to "how relevant is Alice's expertise, right now, to this specific diff."&lt;/p&gt;

&lt;p&gt;Useful inputs for that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Last contribution date&lt;/strong&gt; — when did this person last touch this area?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Number of recent relevant changes&lt;/strong&gt; — not lifetime commits, recent ones. Someone with 3 commits last month is a stronger current signal than someone with 200 commits two years ago.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Similarity to the current PR&lt;/strong&gt; — has this person touched the same files, the same functions, the same data model recently?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review activity&lt;/strong&gt; — have they been reviewing PRs in this area, even if they haven't authored changes there?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Historical ownership&lt;/strong&gt; — were they ever formally responsible for this, even if the file has since changed?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can express this as a rough conceptual model — not a formula anyone should treat as an industry standard, just a way to make the tradeoff explicit:&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;reviewer_relevance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ownership_weight&lt;/span&gt;
                    &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;expertise_weight&lt;/span&gt;
                    &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;recency_weight&lt;/span&gt;
                    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;load_penalty&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual weights are going to be organization-specific, and honestly team-specific within an org. A security-critical path might want ownership weighted heavily no matter what. A fast-moving product surface might want recency and review history to dominate. The point of writing it this way isn't to hand you a formula — it's to make explicit that "who reviews this" is a function of multiple decaying/growing signals, not a single static lookup.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Work Through a Sample PR
&lt;/h2&gt;

&lt;p&gt;Say you've got a repo with three top-level domains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/payments/
/identity/
/notifications/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A PR comes in touching &lt;code&gt;payments/refunds.go&lt;/code&gt;. &lt;code&gt;CODEOWNERS&lt;/code&gt; says &lt;code&gt;payments/&lt;/code&gt; belongs to Engineer A. Here's how three candidate reviewers actually stack up:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Reviewer&lt;/th&gt;
&lt;th&gt;Ownership&lt;/th&gt;
&lt;th&gt;Recent work&lt;/th&gt;
&lt;th&gt;Similar reviews&lt;/th&gt;
&lt;th&gt;Current load&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Engineer A&lt;/strong&gt; is the listed owner, but hasn't done recent work in this directory, and is already carrying a heavy review load.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Engineer B&lt;/strong&gt; isn't in CODEOWNERS at all, but has been actively working in this area recently and has reviewed similar changes — they've probably seen the exact class of bug this PR could introduce.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Engineer C&lt;/strong&gt; has moderate ownership and moderate recent activity, low similarity to this specific change, but has bandwidth right now.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A purely static system routes to A every time, because A is the only name it knows how to check against. But on the actual signals that predict a useful review — recent, hands-on context and demonstrated familiarity with similar changes — B is the stronger candidate, and C is a reasonable fallback if B is unavailable.&lt;/p&gt;

&lt;p&gt;This doesn't mean A should be removed from the review entirely — for something in a sensitive path, keeping the formal owner in the loop for accountability might still make sense. It means A shouldn't be the &lt;em&gt;only&lt;/em&gt; signal the system checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. A Practical Routing Framework
&lt;/h2&gt;

&lt;p&gt;Distilling this into something you could actually reason about (or build) for your own repo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ownership&lt;/strong&gt; — who's formally accountable, per CODEOWNERS or equivalent?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recency&lt;/strong&gt; — who's touched this code lately?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expertise&lt;/strong&gt; — who has a track record of relevant, substantive contributions here?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review history&lt;/strong&gt; — who's reviewed similar changes before, and reviewed them well (not just approved fast)?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load&lt;/strong&gt; — who has the bandwidth to actually do this review in a reasonable timeframe?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of these should unilaterally win. Ownership keeps governance intact — you don't want a recency model quietly routing security-sensitive changes away from the team accountable for them. Load is a modifier, not a primary filter — someone who's slightly busy but the clear best fit is often still the right call over someone who's fully free but has no context. The five signals are meant to be combined, with the weighting depending on what kind of change you're looking at (a one-line config tweak needs way less of this than a cross-cutting refactor).&lt;/p&gt;

&lt;h2&gt;
  
  
  6. The Routing-Decay Loop
&lt;/h2&gt;

&lt;p&gt;Here's the pattern that causes CODEOWNERS files to quietly go stale, laid out as a loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;People change teams / roles
        ↓
Code changes (new services, refactors, deprecations)
        ↓
Expertise shifts (some people gain context, others lose it)
        ↓
Ownership file stays the same (nobody's job to update it proactively)
        ↓
Routing quality declines
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing in that loop is anomalous — it's just normal organizational and codebase churn. The failure isn't that people move around or that code changes. The failure is that CODEOWNERS has no way to hear about any of it. It's a file, not a feed.&lt;/p&gt;

&lt;p&gt;The fix isn't "update the file more often" — that's a losing battle against how fast real orgs change. It's closer to: &lt;strong&gt;continuously derive dynamic signals from data you already have&lt;/strong&gt; (commit history, PR review history, recent activity) and layer them on top of the static file, rather than trying to keep the static file itself perfectly current. Contribution and review data refresh automatically as people work — no one has to remember to edit a YAML-adjacent file when Bob becomes the de facto payments expert. The signal is already sitting in your git history and PR review logs; it's just not being used for routing.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. What Teams Can Measure Today
&lt;/h2&gt;

&lt;p&gt;You don't need new tooling to start getting visibility into how well your current routing is actually working. Most of this is derivable from data already in GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reviewer reassignment rate&lt;/strong&gt; — how often does the auto-assigned reviewer get swapped out before the PR merges? High rate = your default routing isn't matching reality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time to first &lt;em&gt;meaningful&lt;/em&gt; review&lt;/strong&gt; — not first comment, first substantive review. If the assigned reviewer routinely takes days to engage, they may be overloaded or not the right fit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review load concentration&lt;/strong&gt; — what percentage of review volume in a given area lands on one person? If it's consistently one name, you likely have a bottleneck, whether or not that person is technically the best expert.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PRs routed to inactive contributors&lt;/strong&gt; — a rough version of this is: for each CODEOWNERS entry, check &lt;code&gt;git log --since="6 months ago" -- &amp;lt;path&amp;gt;&lt;/code&gt; and see if the listed owner shows up. If not, that entry is a candidate for drift.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review handoffs&lt;/strong&gt; — PRs where the review gets passed from one person to another before merge are a decent proxy for "the first routing decision missed."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recent contribution coverage&lt;/strong&gt; — what share of merged PRs in an area were reviewed by someone who'd actually committed to that area in the last N weeks?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these require a new system to start tracking. A few are a &lt;code&gt;git log&lt;/code&gt; or a GitHub API query away. The value isn't in any single metric — it's in noticing, over a few months, whether your routing is drifting in a direction nobody's paying attention to.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Practical Takeaway
&lt;/h2&gt;

&lt;p&gt;None of this is an argument for dropping CODEOWNERS. It's still the right mechanism for encoding accountability — who's on the hook for a given part of the codebase, and which teams need guaranteed visibility into changes that touch their area.&lt;/p&gt;

&lt;p&gt;What it's not sufficient for, on its own, is figuring out who should actually review a given PR today.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep CODEOWNERS for accountability. Add dynamic signals for relevance.&lt;/strong&gt; Ownership answers "who's responsible." Recency, expertise, review history, and load together answer "who's actually the right person to look at this, right now." A routing setup that only asks the first question will keep sending PRs to people who used to be the right answer. A routing setup that ignores the first question entirely gives up the governance guarantees CODEOWNERS exists to provide. You want both.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;How are you handling reviewer routing today&lt;/strong&gt; — static CODEOWNERS, team rotation, tribal knowledge ("just tag whoever's around"), or something more dynamic? Curious what's actually working at different team sizes — drop your setup in the comments.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>git</category>
      <category>github</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Our AI code reviewer problem wasn't that it was wrong. It was that it wouldn't shut up</title>
      <dc:creator>Codzee.io</dc:creator>
      <pubDate>Thu, 27 Aug 2026 13:16:37 +0000</pubDate>
      <link>https://dev.to/codzee_io/our-ai-code-reviewer-problem-wasnt-that-it-was-wrong-it-was-that-it-wouldnt-shut-up-5b5</link>
      <guid>https://dev.to/codzee_io/our-ai-code-reviewer-problem-wasnt-that-it-was-wrong-it-was-that-it-wouldnt-shut-up-5b5</guid>
      <description>&lt;p&gt;I've been thinking about a problem with AI code review that I don't see discussed enough: a reviewer can be technically correct and still make the whole review experience worse.&lt;/p&gt;

&lt;p&gt;Here's the PR that made this click for me. Small change, maybe 200 lines, nothing exotic. New validation path, a couple helper functions. The kind of thing that used to get a comment or two and an approve.&lt;/p&gt;

&lt;p&gt;The AI reviewer came back with something like a dozen comments. A naming suggestion. A "this null check might be redundant" note. A theoretical race condition that would only show up under conditions the code was never going to hit in prod. A "you could extract this into a function" suggestion on a function that was already about ten lines long.&lt;/p&gt;

&lt;p&gt;And here's the annoying part  most of these weren't &lt;em&gt;&lt;a href="https://www.codzee.io/" rel="noopener noreferrer"&gt;wrong&lt;/a&gt;&lt;/em&gt;. The variable name genuinely could've been clearer. The null check probably was redundant. Individually, I could defend almost every comment on that list.&lt;/p&gt;

&lt;p&gt;But there was one comment in there about an actual edge case in the validation logic that genuinely mattered. And it was sitting in the middle of eleven other comments that didn't. The dev opening the PR skimmed most of it, dismissed a few without really reading them, and nearly skipped the important one too — because by comment seven or eight, he'd already stopped expecting any of it to be worth his time.&lt;/p&gt;

&lt;p&gt;Nobody wanted "more thorough." They wanted to know what actually mattered.&lt;/p&gt;

&lt;p&gt;That's the distinction I think gets missed constantly: &lt;strong&gt;"is this technically an issue" and "is this worth interrupting someone for" are completely different questions&lt;/strong&gt;, and most tooling in this space only ever answers the first one.&lt;/p&gt;

&lt;p&gt;The assumption baked into a lot of AI review tools is basically: if finding more issues is good, finding even more must be better. But review was never just an issue-detection problem, it's a signal-to-noise problem, and honestly kind of a trust problem too. Once a reviewer starts treating a PR like a checklist to maximize, it stops mattering how accurate any individual comment is — the aggregate experience is what people remember, and the aggregate experience was "annoying."&lt;/p&gt;

&lt;p&gt;That frustration eventually became one of the reasons I started working on Codzee. Not going to turn this into a pitch — the short version is the goal isn't "find everything," it's figuring out what actually deserves a developer's attention. Still early, still figuring a lot of it out.&lt;/p&gt;

&lt;p&gt;Genuinely curious how other people think about this though:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How many AI review comments do you actually want to see on a normal PR? Is there a number where it tips from "helpful" to "annoying" for you?&lt;/li&gt;
&lt;li&gt;Do you find yourself tuning out AI-generated comments after a tool has burned you with noise a few times?&lt;/li&gt;
&lt;li&gt;Where's your personal line between a useful warning and something that's just noise dressed up as a finding?&lt;/li&gt;
&lt;li&gt;Would you rather a reviewer miss a low-confidence issue entirely, or flag it anyway and let you decide?&lt;/li&gt;
&lt;li&gt;What actually makes you trust an automated reviewer, versus just tolerate it?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not trying to manufacture a debate here, I'm actually unsure about some of these myself — like the miss-vs-flag question, I go back and forth on what the right default even is. Curious what this sub thinks.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>codequality</category>
      <category>coding</category>
      <category>softwareengineering</category>
    </item>
  </channel>
</rss>
