<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: CodeRabbit</title>
    <description>The latest articles on Forem by CodeRabbit (@coderabbitai).</description>
    <link>https://forem.com/coderabbitai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F7167%2F3c5e8773-7cea-46a9-ae16-841eb6b29b19.png</url>
      <title>Forem: CodeRabbit</title>
      <link>https://forem.com/coderabbitai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/coderabbitai"/>
    <language>en</language>
    <item>
      <title>Why do test coverage metrics keep misleading developers?</title>
      <dc:creator>Obisike Treasure</dc:creator>
      <pubDate>Wed, 22 Apr 2026 12:11:57 +0000</pubDate>
      <link>https://forem.com/coderabbitai/why-do-test-coverage-metrics-keep-misleading-developers-1n6i</link>
      <guid>https://forem.com/coderabbitai/why-do-test-coverage-metrics-keep-misleading-developers-1n6i</guid>
      <description>&lt;p&gt;High test coverage is often seen as a sign of software quality, yet it raises an important question: Why do well-tested applications still have bugs?&lt;/p&gt;

&lt;p&gt;Many assume that high scores on test coverage metrics translates to high-quality software. However, the truth is much different. While test coverage can tell you how much of your code is executed during testing, it does not indicate if those tests are effective or comprehensive enough to detect all bugs and edge cases in the software. &lt;/p&gt;

&lt;p&gt;This article explores why test coverage stats can be misleading and highlights scenarios where test coverage metrics create a false sense of security and quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Are test coverage metrics misleading?
&lt;/h2&gt;

&lt;p&gt;Test coverage metrics are often seen as a measure of testing effectiveness, but they can indeed be misleading. A high coverage percentage might suggest that a system is thoroughly tested, yet it says nothing about how well the tests validate the code. Coverage alone does not account for the quality, depth, or relevance of the tests being executed.&lt;/p&gt;

&lt;p&gt;This is why it's important to look beyond coverage numbers and consider what they actually represent. &lt;/p&gt;

&lt;h3&gt;
  
  
  Test coverage does not equal meaningful testing
&lt;/h3&gt;

&lt;p&gt;Just because a test executes a line of code doesn't mean it's actually testing anything useful, like a car fuel gauge that’s always showing full, even when the tank is empty. It looks fine, but it tells you nothing.&lt;/p&gt;

&lt;p&gt;Test coverage is easy to manipulate to achieve the desired coverage percentage. It doesn't take much effort to write a useless test that surmounts 100%. For example, if you have a function that calculates the price whether given a discount or not:&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;calculatePrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;number&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;discount&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;price&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;discount&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;price&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;Here is a possible test case that gives 100% test coverage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vitest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;calculatePrice&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./calculatePrice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;calculatePrice&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;it adds properly&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;calculatePrice&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;2&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;it adds properly with values higher than zero&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;calculatePrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the result of the test and the coverage:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5hnsn1lqp03979lz1n5b.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.amazonaws.com%2Fuploads%2Farticles%2F5hnsn1lqp03979lz1n5b.png" alt="Screenshot showing the test coverage" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example, you can see that the test cases do not handle edge cases or address the business issue that the price of a commodity can't be negative. Even with that, you still have 100% test coverage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overemphasis on numbers in test coverage
&lt;/h2&gt;

&lt;p&gt;Many companies treat test coverage as a key performance indicator (KPI), using it as a benchmark for assessing the quality of their testing efforts. In some cases, teams are even offered incentives to achieve a specific percentage of coverage. While this approach may seem logical, encouraging developers to write more tests, it often leads to behaviors that undermine the true purpose of software testing.&lt;/p&gt;

&lt;p&gt;By placing too much emphasis on the coverage percentage, organizations shift the focus from writing effective, meaningful tests to merely increasing the test coverage number. This results in developers gaming the system to meet targets rather than ensuring software reliability.&lt;/p&gt;

&lt;p&gt;Consider a scenario where a company mandates 90% test coverage as a KPI, with bonuses tied to achieving this goal. Developers, eager to meet the requirement, may resort to writing tests that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do not assert anything – The test runs but doesn't check any output, allowing coverage metrics to increase artificially.&lt;/li&gt;
&lt;li&gt;Cover trivial code paths – Simple getter and setter functions are tested while complex business logic remains untested.&lt;/li&gt;
&lt;li&gt;Artificially inflate coverage – Tests execute code but ignore potential failures or incorrect results, leading to a false sense of security.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While test coverage metrics may look impressive, the software remains vulnerable to undetected bugs. This misalignment between incentives and real testing needs ultimately results in fragile software that poses potential risks of failure in production despite high coverage numbers.&lt;/p&gt;

&lt;p&gt;This overemphasis on numbers fosters a check-the-box mentality, where the success of testing is judged purely on a percentage rather than its actual effectiveness. It discourages engineers from thinking critically about edge cases, real-world scenarios, and risk-based testing approaches. &lt;/p&gt;

&lt;h2&gt;
  
  
  False sense of security with test coverage
&lt;/h2&gt;

&lt;p&gt;Having a maximum test coverage gives you a false sense of security. High coverage creates a feeling of overconfidence that can cause you to overlook more exploratory testing methods or the important insights gained from peer reviews, as most companies would presume that their software has attained quality status on the application of high test coverage testing. &lt;/p&gt;

&lt;p&gt;It's similar to driving a car with a nice dashboard. Everything may appear to be in order at first glance, but there's no guarantee that the engine is operating properly below.&lt;/p&gt;

&lt;p&gt;For instance,&lt;a href="https://arxiv.org/abs/2109.11921" rel="noopener noreferrer"&gt; a study&lt;/a&gt; titled "Can We Trust Tests To Automate Dependency Updates? A Case Study of Java Projects" examined the effectiveness of test suites in detecting faults related to dependency updates. The researchers found that, despite high test coverage, tests detected only 47% of faults in direct dependencies and 35% in transitive dependencies. &lt;/p&gt;

&lt;p&gt;This indicates that even with substantial test coverage, a significant portion of potential issues remained untested, leading to a misplaced confidence in the code's reliability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signs your test coverage is misleading you
&lt;/h2&gt;

&lt;p&gt;Test coverage scores can be misleading, especially when they fail to reflect real-world software reliability. If coverage numbers are impressive but critical bugs still slip through, it's a sign that your testing strategy needs reevaluation. Here are key indicators that your test coverage might not be as effective as it appears:&lt;/p&gt;

&lt;h3&gt;
  
  
  Frequent regressions despite high test coverage&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;A regression occurs when a previously working feature breaks after changes are made to the codebase, such as adding new features, refactoring, or fixing bugs. While high test coverage may suggest strong protection against regressions, it can be misleading if tests only check whether functions execute rather than validate their actual behavior.&lt;/p&gt;

&lt;p&gt;If test cases do not account for edge cases, business logic variations, or interactions between components, regressions can slip through undetected. This often happens when tests are written to maximize coverage metrics instead of ensuring functional correctness. &lt;/p&gt;

&lt;p&gt;As a result, you may find yourself repeatedly fixing the same issues despite having a high coverage score.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tests that pass even when key functionality breaks
&lt;/h3&gt;

&lt;p&gt;Strong test coverage does not prevent superficial tests from creating false positives. These false positive tests are tests that can pass even when important functionality is broken. They just guarantee that the code executes without evaluating its output or implementing thorough checks on business logic and edge cases.&lt;/p&gt;

&lt;p&gt;Here is an example: &lt;/p&gt;

&lt;p&gt;Let's say you have a function that calculates discounts for an e-commerce checkout system:&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;calculateDiscount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;discountPercentage&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;price&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;discountPercentage&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A poorly written test might only check if the function runs without errors, but not validate the correctness of the output:&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;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;calculateDiscount should not return null&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="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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculateDiscount&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBeNull&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;Here is why this test is misleading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This test will pass even if the function is completely wrong because it only verifies that a value is returned.&lt;/li&gt;
&lt;li&gt;If a developer mistakenly changes the function to always return 0, like this:
&lt;/li&gt;
&lt;/ul&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;calculateDiscount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;discountPercentage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&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;The test will still pass despite the broken discount logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test suites are bloated with low-value tests that don't catch critical bugs
&lt;/h3&gt;

&lt;p&gt;Tests that are written superficially and do not adequately identify edge situations in a software's functionality defeat the objective of testing and lead to software bloating. Bloating without obvious justification can have a detrimental impact on the software's speed, performance, and, eventually, adoption by end users/customers.&lt;/p&gt;

&lt;p&gt;Here's an example:&lt;/p&gt;

&lt;p&gt;A function is supposed to process an order by applying a discount, but because of a bug, it never subtracts the discount from the total:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Function intended to process an order with a discount.&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}):&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Bug: The discount is ignored; the total is returned as is.&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;Here are the test cases:&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;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;processOrder&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;returns the total when discount is provided&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="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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;processOrder&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;total&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="na"&gt;discount&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&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="p"&gt;});&lt;/span&gt;

 &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;returns the total when no discount is provided&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="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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;processOrder&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that, you'll still get 100% test coverage:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7wnelazd76gn6t4iljvr.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.amazonaws.com%2Fuploads%2Farticles%2F7wnelazd76gn6t4iljvr.png" alt="Screenshot of the test coverage" width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example, the test suite may indicate excellent coverage since it runs every line of the code, but it fails to detect the critical bug in which the discount is never applied. It also does not account for edge case values, like cases where the total or discount is zero, negative numbers for total or discount, and cases where the discount is greater than the total.&lt;/p&gt;

&lt;h3&gt;
  
  
  Over-reliance on mocks and stubs
&lt;/h3&gt;

&lt;p&gt;One critical issue in testing is the overuse of mocks and stubs. Modern software often relies on numerous external dependencies, and to test components that interact with these dependencies, developers commonly use mocks and stubs. While this approach can make testing more efficient, it comes with significant risks.&lt;/p&gt;

&lt;p&gt;The problem lies in the assumption that developers fully understand the behavior of the mocked dependency, including its edge cases and quirks. In reality, this is nearly impossible. As a result, many unexpected behaviors, failure scenarios, and integration issues may never be tested. This creates a false sense of confidence, where tests pass but fail to reflect real-world conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don’t chase high test coverage
&lt;/h2&gt;

&lt;p&gt;Instead of chasing high test coverage percentages, follow these practical strategies to improve test effectiveness and real-world reliability. Here's what you should prioritize to build a stronger, more meaningful test suite:&lt;/p&gt;

&lt;h3&gt;
  
  
  Test quality over quantity
&lt;/h3&gt;

&lt;p&gt;In testing, quality matters more than quantity. High test coverage may look impressive, but if tests only confirm that code executes without validating behavior, they provide little real protection. Instead of chasing coverage metrics, focus on writing tests that verify expected outcomes.&lt;/p&gt;

&lt;p&gt;Using the &lt;code&gt;calculateDiscount&lt;/code&gt; function 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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;calculateDiscount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;discountPercent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;number&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;discountPercent&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Discount percent cannot be negative&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="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;discountPercent&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A low-quality test would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vitest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;calculateDiscount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./calculateDiscount&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Low-quality test: only determines if the function runs without error.&lt;/span&gt;
&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Low-Quality Test&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should run without throwing an error&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// This test only calls the function without asserting correctness.&lt;/span&gt;
    &lt;span class="nf"&gt;calculateDiscount&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;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While a high-quality test to verify the proper results will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// High-quality tests: verifying the intended behavior.&lt;/span&gt;
&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;High-Quality Tests&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;returns correct discount for valid input&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;calculateDiscount&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;20&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;returns full price when discount is 0&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;calculateDiscount&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;toBe&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="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;returns 0 for a 100% discount&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;calculateDiscount&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;toBe&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="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;throws error for negative discount&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;calculateDiscount&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;toThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Discount percent cannot be negative&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The high-quality test covers most edge cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Combine test coverage with other test metrics
&lt;/h3&gt;

&lt;p&gt;Relying solely on test coverage can lead to blind spots in your testing strategy. To make sure your tests are effective, supplement coverage with other key quality indicators:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Peer Reviews and Exploratory Testing – Manual testing and developer reviews help uncover edge cases that automated tests might overlook.&lt;/li&gt;
&lt;li&gt;Code Complexity Analysis – Highly complex code demands more rigorous testing, even if coverage is high.&lt;/li&gt;
&lt;li&gt;Bug Frequency and Production Issues – If frequently covered code still leads to real-world failures, your tests may not be meaningful.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, an order processing module might show high test coverage but still allow critical issues like duplicate orders, payment failures, or unexpected behavior under network failures. In such cases, expand your test suite to include edge cases, integration failures, and real-world scenarios rather than just ensuring the code runs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Contextual testing
&lt;/h3&gt;

&lt;p&gt;Test coverage alone does not guarantee that an application functions correctly under real-world conditions. Contextual testing aligns tests with the business logic and practical usage scenarios, rather than just verifying code execution.&lt;/p&gt;

&lt;p&gt;For example, consider a login function. A superficial test might only check whether the function runs without errors. However, a contextual test should verify that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Valid credentials return a session token.&lt;/li&gt;
&lt;li&gt;Invalid credentials trigger the correct error message.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example: Contextual Testing for a Login Function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A login function should behave as expected under different conditions.&lt;br&gt;
Implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Returns a JWT-like token for valid credentials&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;username&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;valid_user&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="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;valid_pass&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9&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;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid credentials provided.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vitest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Login Function - Contextual Testing&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;successful login returns a token&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="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;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;valid_user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;valid_pass&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eyJ&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed login throws an error with proper message&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;valid_user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wrong_pass&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid credentials provided.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of merely checking if the function executes, these tests mirror real-world authentication behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They validate that correct credentials return a properly structured token, mimicking a real authentication flow.&lt;/li&gt;
&lt;li&gt;They confirm that incorrect credentials result in an error message, just as a real login system would behave.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While these tests add meaningful validation, they still rely on a controlled, isolated function. In real-world applications, authentication involves external dependencies like databases and APIs. Overuse of stubs and mocks in testing can lead to unrealistic test scenarios. To maintain reliability, you should focus on&lt;a href="https://www.techtarget.com/searchsoftwarequality/definition/integration-testing#:~:text=Integration%20testing%20%2D%2D%20also%20known,tested%20as%20a%20combined%20entity." rel="noopener noreferrer"&gt; integration testing&lt;/a&gt;, where components like user authentication services, databases, and external APIs work together as expected.&lt;/p&gt;

&lt;p&gt;Using technologies like Artificial Intelligence (AI) can improve software testing by identifying weak spots, analyzing patterns, and improving the test process. Beyond the normal coverage metrics, AI-driven tools offer smarter ways to detect potential issues before they become costly problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  What AI tools can do for test coverage
&lt;/h2&gt;

&lt;p&gt;AI introduces a smarter way to detect potential issues before they become costly problems. It reduces manual efforts and can perform complex analyses, including&lt;a href="https://www.coderabbit.ai/blog/static-code-analyzers-vs-ai-code-reviewers-which-is-best" rel="noopener noreferrer"&gt; static code analysis&lt;/a&gt;, thanks to the vast knowledge it's trained with.&lt;/p&gt;

&lt;p&gt;For example, suppose your team submits a pull request (PR) to add a new discount checker function but forgets to include a write test for an edge case. AI can analyze the PR, compare it to your repository's existing test suite, and flag missing test coverage and edge cases that are not accounted for. &lt;/p&gt;

&lt;p&gt;Below, is an example of a test coverage summary that provides an overview of which functions are covered and highlights coverage gaps in the codebase&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Five6geht4hlm4podlrd1.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.amazonaws.com%2Fuploads%2Farticles%2Five6geht4hlm4podlrd1.png" alt="Screenshot showing the test coverage summary" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, it will provide a detailed breakdown, function-by-function, displaying which logic branches and conditions are tested or not.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffbez2eb0dj3o16qaxwoj.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.amazonaws.com%2Fuploads%2Farticles%2Ffbez2eb0dj3o16qaxwoj.png" alt="Screenshot showing AI’s given detailed breakdown" width="800" height="553"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If AI detects untested functions or missing edge case tests, or even test cases written just to increase the test coverage numbers, it flags them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7mv74e1p35aab7obq7py.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.amazonaws.com%2Fuploads%2Farticles%2F7mv74e1p35aab7obq7py.png" alt="Screenshot of AI showing the issues with the test" width="800" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AI doesn't just identify issues, it provides suggestions on how you can improve your testing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frma6ch1e6mc9q6qjvd8d.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.amazonaws.com%2Fuploads%2Farticles%2Frma6ch1e6mc9q6qjvd8d.png" alt="Screenshot of AI showing recommendations to improve the test process and coverage" width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Beyond automating repetitive tasks, AI helps you write better tests by pinpointing gaps and refining existing ones. It can recommend test cases for complex logic, flag potential bugs, and even predict areas of risk based on past failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test coverage: Quantity doesn't equal quality
&lt;/h2&gt;

&lt;p&gt;Although test coverage is a helpful indicator of how much of our code has been tested, it's crucial to keep in mind that it’s just one component of the whole picture. High test coverage may seem fantastic, but it doesn't mean that your tests are truly examining the important sections of your code or identifying possible problems.&lt;/p&gt;

&lt;p&gt;Writing comprehensive and insightful tests that verify functionality, edge cases, and business logic is where the true value lies. The emphasis should be on making sure that each test has a purpose and increases confidence in the accuracy of your application rather than being fixated on reaching a large percentage of coverage. Ultimately, when used carefully, test coverage can contribute significantly to a more robust and dependable development process.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>codereview</category>
      <category>coderabbit</category>
      <category>testing</category>
    </item>
    <item>
      <title>How to use AI to identify and fix security vulnerabilities in your codebase</title>
      <dc:creator>Damilola Oshungboye</dc:creator>
      <pubDate>Wed, 22 Apr 2026 09:40:52 +0000</pubDate>
      <link>https://forem.com/coderabbitai/how-to-use-ai-to-identify-and-fix-security-vulnerabilities-in-your-codebase-4na2</link>
      <guid>https://forem.com/coderabbitai/how-to-use-ai-to-identify-and-fix-security-vulnerabilities-in-your-codebase-4na2</guid>
      <description>&lt;p&gt;&lt;em&gt;Meta: Understand the common code-based security vulnerabilities, from SQL injection to XSS, and how AI simplifies the detection and resolution of these security vulnerabilities for improved code security.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With the average &lt;a href="https://insurica.com/blog/average-data-breach-cost-hits-all-time-high-of-4-4m/" rel="noopener noreferrer"&gt;data breach now costing companies $4.45 million,&lt;/a&gt; securing your code has never been more urgent. &lt;/p&gt;

&lt;p&gt;As development cycles accelerate, security vulnerabilities like SQL injections or cross-site scripting (XSS) are still common or discovered too late. AI is changing that. By scanning large codebases, learning from actual attack patterns, and offering targeted fixes, AI tools help you address code security flaws faster than ever.&lt;/p&gt;

&lt;p&gt;This article explores where traditional approaches fall short, how AI can fill those gaps, and the practical steps to embedding AI code security into your development workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common security vulnerabilities in your codebase
&lt;/h2&gt;

&lt;p&gt;When you look at the &lt;a href="https://owasp.org/www-project-top-ten/" rel="noopener noreferrer"&gt;OWASP Top 10&lt;/a&gt;, you’ll notice how many serious threats come from your routine everyday coding patterns. Let’s examine a few of the most prevalent vulnerabilities:&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://owasp.org/www-community/attacks/SQL_Injection" rel="noopener noreferrer"&gt;SQL injection&lt;/a&gt; can be especially dangerous because it exploits the mechanism you rely on to store and retrieve data. An attacker essentially “&lt;strong&gt;injects&lt;/strong&gt;” malicious SQL commands into an application’s inputs, potentially gaining unauthorized access to or altering the underlying data.&lt;/p&gt;

&lt;p&gt;Here’s an example from a simple authentication routine:&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;# auth_service.py
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;authenticate_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&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 id, role FROM users 
        WHERE username = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; 
        AND password = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;database&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code appears functional but is highly vulnerable to SQL injection attacks. The &lt;code&gt;authenticate_user&lt;/code&gt; function constructs an SQL query using string concatenation, which allows an attacker to inject malicious SQL code. If an attacker inputs ' OR '1'='1 as the username, the query becomes:&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;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;role&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="s1"&gt;'1'&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'1'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'anything'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query will always return true, allowing the attacker to potentially bypass authentication and access protected data. The consequences of SQL injection attacks can be severe, including loss of confidentiality, tampering with existing data, identity spoofing, and even gaining administrative access to the database server.&lt;/p&gt;

&lt;p&gt;A more secure approach uses parameterized queries:&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;# auth_service.py (secure version)
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;authenticate_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        SELECT id, role FROM users 
        WHERE username = %s AND password = %s
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;database&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="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this secure version, the SQL query uses placeholders (%s) for the user input, and the actual values are passed as parameters to the execute method. This approach ensures that the input data is properly escaped and cannot be used to manipulate the SQL query structure&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Cross-site scripting (XSS)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://owasp.org/www-community/attacks/xss/" rel="noopener noreferrer"&gt;XSS&lt;/a&gt; happens when attackers inject malicious scripts (often JavaScript) into web pages that other users view. Any spot in your application where user input is rendered onto the page can be a gateway for XSS.&lt;/p&gt;

&lt;p&gt;The following example of a blog post display function is a common pattern in content management systems and is vulnerable to XSS attacks.&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.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/blog/&amp;lt;post_id&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;display_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&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;
        &amp;lt;article&amp;gt;
            &amp;lt;h1&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/h1&amp;gt;
            &amp;lt;div&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/div&amp;gt;
        &amp;lt;/article&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An attacker could inject malicious JavaScript through the post content, affecting every visitor.&lt;/p&gt;

&lt;p&gt;In this example, an attacker could script malicious JavaScript through the post.content field. For instance, if an attacker submits a blog post with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;XSS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script will be executed by every visitor who views the blog post, allowing the attacker to steal session cookies, manipulate the user's browser, or perform other malicious actions.&lt;/p&gt;

&lt;p&gt;To counter XSS, always sanitize and escape user-generated content:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/blog/&amp;lt;post_id&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;display_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;post.html&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&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;The escape function from the markupsafe library is used to ensure that any user-provided content is properly sanitized, preventing malicious scripts from being executed in the user's browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Hardcoded secrets
&lt;/h3&gt;

&lt;p&gt;Hardcoding sensitive information, like API keys or database credentials, into your code is a common mistake, often done for convenience. However, attackers can use these secrets to gain unauthorized access if this code ends up in a public repository or is leaked elsewhere.&lt;/p&gt;

&lt;p&gt;Here is a simple but risky approach:&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;class&lt;/span&gt; &lt;span class="nc"&gt;StorageService&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;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AKIA1234567890ABCDEF&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jK8*2nP9$mB4#kL5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;aws_access_key_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;aws_secret_access_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws_secret&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If these credentials appear in a publicly accessible repository, malicious actors can exploit them immediately. Instead, you can store secrets in environment variables or a secure secrets manager:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StorageService&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;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;aws_access_key_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS_ACCESS_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;aws_secret_access_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS_SECRET_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The decouple library from config is used to load the AWS credentials from environment variables, ensuring that sensitive information is not hardcoded in the application code. This approach significantly reduces the risk of credential exposure and subsequent security breaches.&lt;/p&gt;

&lt;p&gt;Although these vulnerabilities may appear obvious individually, they become difficult to detect within large codebases, especially when many developers make multiple commits daily across various repositories. Manual reviews alone struggle to consistently catch security issues at scale.&lt;/p&gt;

&lt;p&gt;Keeping these vulnerabilities in mind, let's examine how various types of security testing can help identify and prevent them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing static or dynamic code analysis?
&lt;/h2&gt;

&lt;p&gt;It’s critical to identify vulnerabilities early. Two practical approaches to scrutinizing your code are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://circleci.com/blog/static-application-security-testing-sast/" rel="noopener noreferrer"&gt;&lt;strong&gt;Static Application Security Testing&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;(SAST)&lt;/strong&gt; – Analyzes your codebase without executing it, pinpointing issues like insecure coding patterns, hardcoded secrets, or known vulnerability signatures.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://circleci.com/blog/dynamic-application-security-testing-dast/" rel="noopener noreferrer"&gt;&lt;strong&gt;Dynamic Application Security Testing&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;(DAST)&lt;/strong&gt; – Interacts with your running application to spot real-time issues, such as misconfigurations or runtime injection paths.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Many security teams rely on both since SAST focuses on code structure, whereas DAST uncovers flaws that are only visible during runtime. Whichever approach you take, the idea is to catch issues well before affecting production users.&lt;/p&gt;

&lt;h2&gt;
  
  
  An AI-assisted approach to code security
&lt;/h2&gt;

&lt;p&gt;Even if you’re vigilant about security, modern codebases constantly evolve, and it’s easy for vulnerabilities to slip through. AI code review tools help by quickly scanning large repositories, drawing on known attack patterns, and machine learning to spot issues you might otherwise miss.&lt;/p&gt;

&lt;p&gt;They also offer practical suggestions for fixing those issues, reinforcing your overall security posture.&lt;/p&gt;

&lt;p&gt;To see this in action, consider a Python Flask project that handles user profiles and file uploads (two areas where security oversights often hide). AI can highlight these hidden risks and guide you in resolving them before they become real problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up an AI tool for automated code reviews for security vulnerabilities
&lt;/h2&gt;

&lt;p&gt;If you’d like to explore AI-based reviews, you can try any tool that suits your needs; some include &lt;a href="https://github.com/features/copilot" rel="noopener noreferrer"&gt;GitHub Copilot&lt;/a&gt;, &lt;a href="https://claude.ai/" rel="noopener noreferrer"&gt;Claude&lt;/a&gt;, and &lt;a href="https://www.perplexity.ai/" rel="noopener noreferrer"&gt;Perplexity.&lt;/a&gt; For this tutorial, we’ll use a popular AI tool like &lt;a href="https://chatgpt.com/" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt; to review and examine common vulnerabilities with a Python Flask application that manages user profiles and handles photo uploads.&lt;/p&gt;

&lt;p&gt;First, clone the Python project that handles user profile management.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Tabintel/py-photo-lib.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to the project directory and install dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;py-photo-lib
python &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv
venv&lt;span class="se"&gt;\S&lt;/span&gt;cripts&lt;span class="se"&gt;\a&lt;/span&gt;ctivate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, create a branch for the new photo upload functionality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; feature/profile-uploads
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After cloning the repository and creating the branch, copy the code from this &lt;a href="https://gist.github.com/Tabintel/0afac74f60b27e8b1212baa378e788ab" rel="noopener noreferrer"&gt;GitHub gist&lt;/a&gt; into your app.py file. &lt;/p&gt;

&lt;p&gt;This feature adds photo upload capabilities to the application, allowing users to upload profile images.&lt;/p&gt;

&lt;p&gt;The changes include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New route /upload for uploading images.
&lt;/li&gt;
&lt;li&gt;File upload handling using Flask's request.files.
&lt;/li&gt;
&lt;li&gt;Database query in /profile/&amp;lt;username&amp;gt; to fetch user details.
&lt;/li&gt;
&lt;li&gt;Configured API to handle profile image paths.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before pushing the code to the main repository, you’ll first use an AI tool to review it for any vulnerabilities.&lt;/p&gt;

&lt;p&gt;Give this prompt to your AI tool (we’re using ChatGPT in this case):&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Review the following code, which adds a new profile image upload feature to a Python Flask application. Identify any security vulnerabilities or potential risks in the implementation, briefly explain why, and suggest improvements where necessary."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the review process, you would likely receive precise, line-by-line recommendations, such as these examples:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Exception handling enhancement
&lt;/h3&gt;

&lt;p&gt;The review flagged broad exception handling, suggesting you implement more specific error tracking to prevent the exposure of sensitive information. &lt;/p&gt;

&lt;p&gt;This is a critical security concern, as broad exception handling can make identifying and addressing potential issues difficult. To address this, you can implement a robust exception-handling mechanism that includes specific error messages and logging.&lt;/p&gt;

&lt;p&gt;For example, instead of using a broad exception handler like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can implement a more specific exception handler that includes error messages and logging:&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;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ValueError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Log the exception and return an error message
&lt;/span&gt;    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Invalid value: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Invalid value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Log the exception and continue
&lt;/span&gt;    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;An error occurred: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach ensures that each exception type is handled specifically, providing more detailed error logs and making diagnosing and fixing issues easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Undefined username
&lt;/h3&gt;

&lt;p&gt;Another vulnerability identified is related to undefined model imports. Having undefined model imports in any codebase can make it difficult to ensure data integrity and prevent unauthorized access. &lt;/p&gt;

&lt;p&gt;To address this, you can enhance the username validation to prevent undefined model imports.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;User&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;User&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the User models are imported correctly, you prevent potential errors that could arise from undefined models, which can lead to data inconsistencies and prevent attacks like SQL injections.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Additional code vulnerabilities
&lt;/h3&gt;

&lt;p&gt;From this particular review, you can see that the AI tool recognizes areas in the app.py code pull request and points out the exact lines of code that the changes need to be reviewed for security purposes. Let’s look at each of them:&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Image format validation
&lt;/h3&gt;

&lt;p&gt;The automated review flags the need to review image format validation. Currently, the application only allows png, jpg, jpeg, and gif formats for uploaded photos:&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;ALLOWED_EXTENSIONS&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;png&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;jpg&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;jpeg&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;gif&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;MAX_FILE_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;  &lt;span class="c1"&gt;# 5MB
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this approach is sufficient for basic use cases, it leaves the application vulnerable to limitations or potential misuse. &lt;/p&gt;

&lt;p&gt;For example, if the project requires support for other formats (e.g., webp, tiff), the lack of validation for these could result in errors or even security risks when unexpected file types are uploaded. &lt;/p&gt;

&lt;p&gt;Proper validation ensures that the system explicitly handles supported formats, mitigating risks from unverified file uploads.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Function usage documentation
&lt;/h3&gt;

&lt;p&gt;Next, there’s a suggestion to clarify function usage in docstring. A short docstring for allowed_file could be helpful in describing its purpose and the expected parameter or return types.&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;allowed_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; \
           &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rsplit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ALLOWED_EXTENSIONS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without documentation, you may misunderstand the purpose or behavior of this function. Adding a short docstring that specifies the function's role (validating file extensions) and details the expected parameters and return values would prevent miscommunication and misuse.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Image processing logging
&lt;/h3&gt;

&lt;p&gt;There's a suggestion to validate and log image processing steps. The image processing logic is good but may benefit from explicit logging when conversions or resizing occur, especially to diagnose user-upload issues.&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;process_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Convert to RGB if necessary
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;RGB&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;RGB&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Resize if too large
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;thumbnail&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="c1"&gt;# Save optimized version
&lt;/span&gt;        &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;JPEG&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quality&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;optimize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without logs, it becomes difficult to diagnose issues when image uploads fail. Logging each step would provide a clear trail for troubleshooting and prevent vulnerabilities caused by incomplete or incorrect processing. Logs also enhance accountability and allow developers to detect unexpected behavior during uploads.&lt;/p&gt;

&lt;p&gt;Through this automated code review process, you've seen how AI tools comprehensively analyze the code, highlighting vulnerabilities in the photo upload feature. The review shows critical security gaps, from unhandled exceptions that could expose system information to unsecured file type validation that might allow malicious uploads and missing model imports that could compromise data integrity. &lt;/p&gt;

&lt;p&gt;Once you've added the code from the review, commit and push the changes to your GitHub repository using the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add app.py
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"feat: add profile photo upload functionality"&lt;/span&gt;
git push origin feature/profile-uploads
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementing code security best practices
&lt;/h2&gt;

&lt;p&gt;Security requires ongoing diligence. Here are some routines you can integrate into daily development:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Regular code reviews
&lt;/h3&gt;

&lt;p&gt;Peer reviews remain indispensable for catching logical errors and sharing knowledge. To maximize coverage, combine them with automated scanning.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Secure coding standards
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Avoid storing secrets in code.

&lt;ul&gt;
&lt;li&gt;Enforce the least privilege for database connections.
&lt;/li&gt;
&lt;li&gt;Use parameterized queries by default.
&lt;/li&gt;
&lt;li&gt;These guidelines reduce the risk of common issues slipping through.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Frequent vulnerability assessments
&lt;/h3&gt;

&lt;p&gt;Schedule periodic scans (e.g., monthly or quarterly) using SAST and DAST tools. Consider penetration tests for critical areas of your application.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Automate where possible
&lt;/h3&gt;

&lt;p&gt;CI/CD pipelines can automatically run security tests before deployment, ensuring no commit merges without scrutiny. AI can assist in triaging results, making the process more efficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Embrace DevOps culture
&lt;/h3&gt;

&lt;p&gt;Encourage open collaboration between development and operations, ensuring that security is baked in from the start rather than bolted on at the end.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Shift-Left security
&lt;/h3&gt;

&lt;p&gt;Moving security checks to earlier stages of development (DevSecOps) reduces last-minute surprises. Addressing security vulnerabilities earlier in the development process significantly reduces the time and resources required for remediation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up: Securing your codebase
&lt;/h2&gt;

&lt;p&gt;Applying thoughtful, ongoing code security practices and AI code review checks can significantly reduce your application’s vulnerability. Whether it’s preventing SQL injection, mitigating XSS, or ensuring secrets don’t leak, each layer of protection adds up.&lt;/p&gt;

&lt;p&gt;As you refine these habits and explore AI code security solutions, you’ll discover more efficient ways to keep your applications secure, protect sensitive data, and maintain a reliable development pipeline.   &lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>vulnerabilities</category>
      <category>code</category>
    </item>
    <item>
      <title>When should you use canary deployments?</title>
      <dc:creator>Doug Sillars</dc:creator>
      <pubDate>Mon, 20 Apr 2026 18:42:22 +0000</pubDate>
      <link>https://forem.com/coderabbitai/when-should-you-use-canary-deployments-2ch9</link>
      <guid>https://forem.com/coderabbitai/when-should-you-use-canary-deployments-2ch9</guid>
      <description>&lt;p&gt;One solution for a tricky or high-risk deployment is to leverage a canary deployment. In a canary deployment, new features are rolled out to a small subset of your customer base with no fanfare. If things go well: hooray! Canary deployments are ideal for times when a deployment might go sideways or the feature might not work as expected. By utilizing a canary release, you have minimized the risk, as impacting a small fraction of your users is a lot better than affecting all of them.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvx8m8c1v3v3l5p1xt6w2.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.amazonaws.com%2Fuploads%2Farticles%2Fvx8m8c1v3v3l5p1xt6w2.png" alt="Deploying a Canary Release" width="512" height="236"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post will discuss when development teams might choose to use canary deployments in their applications, what is a canary deployment, common pitfalls to avoid, and how modern tooling can help ease the rollout of canary deployments.&lt;/p&gt;

&lt;h1&gt;
  
  
  What is a canary deployment?
&lt;/h1&gt;

&lt;p&gt;Where does the term canary deployment come from? You may have heard of the term "canary in the coal mine." Early coal miners would keep caged songbirds in the mine with them, because these small birds are highly susceptible to toxic gases. If the canaries suddenly died, the miners knew that there was a dangerous gas release, and they could evacuate the mine before they succumbed to the deadly gas.&lt;/p&gt;

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

&lt;p&gt;A canary deployment can be performed when there is concern about the risks involved. Perhaps you are rolling out new cloud infrastructure that has not yet been tested with the production environment, or maybe there are performance concerns—the updates might cause the website's performance to drop to a terrible crawl. Perhaps the change is small, but it could not be tested for security vulnerabilities. A failed rollout to your entire customer base could end in disaster in all of these cases.&lt;/p&gt;

&lt;h1&gt;
  
  
  Is your team already using canary deployments?
&lt;/h1&gt;

&lt;p&gt;Many teams practice canary-style releases without explicitly calling them that. Common variations include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blue-green deployments&lt;/strong&gt;: Two production environments run in parallel. The blue environment serves the stable version, while green runs the new version. Traffic is gradually shifted from blue to green, limiting risk and enabling fast rollback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Experimental deployments&lt;/strong&gt;: New behavior is exposed to a defined group of users, often with explicit awareness or opt-in. These are typically broader than canary deployments and focus on measuring user impact rather than system health alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gradual rollouts (progressive delivery)&lt;/strong&gt;: After validating a release with a small subset of users, traffic is incrementally increased (for example, 5% → 25% → 100%) while monitoring key metrics like error rates and latency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shadow deployments&lt;/strong&gt;: The new version runs in parallel and receives a copy of real production traffic, but responses are not served to users. This is useful for validation and performance testing without customer impact.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Planning a canary deployment
&lt;/h1&gt;

&lt;p&gt;Imagine this situation: You are about to launch a new feature that is considered high-risk for some reason. The team has opted for a canary deployment, releasing it to a limited group of users first. Achieving a successful deployment in this scenario demands meticulous planning.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deployment audience&lt;/strong&gt;: Who gets the update? This requires a basic knowledge of your audience and understanding of who would benefit the most from the change. If your new version is an overhaul of the mobile dashboard, and the canary release is delivered to desktop users, how can you determine if the deployment was successful? It is certainly useful to have some desktop users in the cohort (in case something breaks on desktop), but in order to best understand the results of the deployment, you want to ensure that users interact with the features of the new version.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Duration&lt;/strong&gt;: How long will the test run? An hour? A week? Depending on the type of deployment, you may know the success/failure quickly. (Did the database correctly sync with the servers?). However, sometimes it may take days to determine success. (Is website performance faster during busy periods?).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Metrics&lt;/strong&gt;: What will the team track during the deployment? What metrics will be considered a success? What metrics would be branded a partial success? When does the team admit failure and roll back the deployment to fix bugs?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Feature flags&lt;/strong&gt;: For some canary deployments, using feature flags can be helpful. Feature flags are tools in your code that manage the deployment of application features to specific audiences. If your team is planning to use canary deployment extensively, it may be useful to integrate feature flags into your codebase.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Deployment risks
&lt;/h1&gt;

&lt;p&gt;With any release, there is a risk of failure. Using a canary deployment mitigates the risk by reducing the blast radius of a failure to a small subset of users—however, even canary releases have risks. Among these are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Service outages&lt;/strong&gt;: If the deployment goes bad, the application may slow down or even cease to work for the selected users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security breaches&lt;/strong&gt;: The new release may introduce a security vulnerability that was not discovered in testing.
Rollback mechanism: Many canary releases do not have a formalized rollback plan, should things go wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data loss&lt;/strong&gt;: If the canary rollout is using a new database or a new interface to the database, any issue with the rollout may introduce data inconsistencies or data loss.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For any release, it is important to consider the risks and how they will be mitigated.  While the canary release lowers the risks, having plans for rollback, handling data loss etc. should be made prior to the release.&lt;/p&gt;

&lt;h1&gt;
  
  
  Key metrics during canary deployments
&lt;/h1&gt;

&lt;p&gt;During any release, it is critical to monitor your systems in case of any trouble during the deployment. Canary releases are no different, although the experimental nature of the release means there is a higher likelihood of issues arising:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Error rates&lt;/strong&gt;: Are your systems throwing more errors than typically seen in production?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System metrics&lt;/strong&gt;: Keep a close eye on memory, CPU, database, and API queries, and other resource metrics. We've all been a part of a release where the new code introduced a memory leak, or suddenly API usage was pegging 400% of normal. By monitoring these in the canary release, the issues can be quickly remediated for the canary users before releasing to the entire base.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traffic distribution&lt;/strong&gt;: Is the canary release being properly served to the selected users? Are users accidentally crossing between the two environments? Handling these issues early ensures that the new build is completely isolated from the old environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security metrics&lt;/strong&gt;: Is the login behavior different in the canary build? Are users interacting with this build differently than the previous version? Could this imply a potential threat?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Monitoring these metrics during the deployment and after the release can help your team better understand the wins (and potential improvements to your app) that occur during the canary release. In addition to traditional monitoring, there are now AI tools that can be used to compare your two environments, quickly parsing the data and determining differences from the old version and the new software version that might be difficult to see in traditional logging.&lt;/p&gt;

&lt;h1&gt;
  
  
  Canary deployments: What's next?
&lt;/h1&gt;

&lt;p&gt;When dealing with a high-risk release, a canary deployment to a small subset of users is a great way to mitigate exposure in production. To be successful in your canary release, be sure to discuss how you will choose the cohort of users, monitor the release, and plan a rollback strategy prior to the release. Then, during the deployment process, your team can track the essential metrics to understand if the canary deployment was a success. Should the deployment go well, the team can plan to add users to the new version of the software, slowly deprecating the old version.&lt;/p&gt;

&lt;p&gt;Developers who use canary releases for risky deployments are able to test problematic code or infrastructure to a small user base, pushing the product ahead while mitigating the risk of an outage.&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>devops</category>
      <category>sre</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Show me the prompt: What to know about prompt requests</title>
      <dc:creator>Arindam Majumder </dc:creator>
      <pubDate>Thu, 29 Jan 2026 05:34:00 +0000</pubDate>
      <link>https://forem.com/coderabbitai/show-me-the-prompt-what-to-know-about-prompt-requests-2fo6</link>
      <guid>https://forem.com/coderabbitai/show-me-the-prompt-what-to-know-about-prompt-requests-2fo6</guid>
      <description>&lt;p&gt;In the 1996 film Jerry Maguire, Tom Cruise’s famous phone call, where he shouts “Show me the money!” cuts through everything else. It’s the moment accountability enters the room.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgky6j4fenyk94pfk88ym.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.amazonaws.com%2Fuploads%2Farticles%2Fgky6j4fenyk94pfk88ym.png" alt="Image" width="680" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In AI-assisted software development, “show me the prompt” should play a similar role.&lt;/p&gt;

&lt;p&gt;As more code is generated by large language models (LLMs), accountability does not disappear. It moves upstream. The question facing modern engineering teams is not whether AI-generated code can be reviewed, but where and how review should happen when intent is increasingly expressed before code exists at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Twitter debate: Prompts versus pull requests
&lt;/h2&gt;

&lt;p&gt;Earlier this week, Gergely Orosz of &lt;a href="https://www.pragmaticengineer.com/" rel="noopener noreferrer"&gt;Pragmatic Engineer&lt;/a&gt; shared a quote on Twitter (or X, if you prefer) from an upcoming podcast with &lt;a href="https://steipete.me/" rel="noopener noreferrer"&gt;Peter Steinberger&lt;/a&gt;, creator of the self-hosted AI agent &lt;a href="https://github.com/clawdbot/clawdbot" rel="noopener noreferrer"&gt;Clawdbot&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg8lctctwsg73rp4orj7h.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.amazonaws.com%2Fuploads%2Farticles%2Fg8lctctwsg73rp4orj7h.png" alt="Image" width="673" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Steinberger’s point was straightforward but provocative: as more code is produced with LLMs, traditional pull requests may no longer be the best way to review changes. Instead, he suggested, reviewers should be given the prompt that generated the change.&lt;/p&gt;

&lt;p&gt;That idea quickly triggered a polarized response.&lt;/p&gt;

&lt;p&gt;Supporters argued that reviewing large, AI-generated diffs is becoming increasingly impractical.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbg5vrn5i2qomgu6aua3i.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.amazonaws.com%2Fuploads%2Farticles%2Fbg5vrn5i2qomgu6aua3i.png" alt="Image" width="668" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From their perspective, the prompt captures intent more directly than the output. It tells reviewers what the developer was trying to accomplish, what constraints they set, and what scope they intended. In addition, a prompt can be re-run or adjusted, which makes it easier to validate the approach without combing through thousands of lines of generated code.&lt;/p&gt;

&lt;p&gt;Critics, however, pointed to issues that prompts alone do not solve: determinism, reproducibility, git blame, and legal accountability.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1qouawyh0mfgva4gsi7f.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.amazonaws.com%2Fuploads%2Farticles%2F1qouawyh0mfgva4gsi7f.png" alt="Image" width="665" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because LLM outputs can vary across runs, models, and configurations, approving a prompt does not necessarily mean approving the exact code that ultimately ships. For audits, ownership, and downstream liability, that distinction matters. In their view, code review cannot be replaced by “prompt approval” without weakening the guarantees that PR-based workflows were designed to provide.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ikxd0krwg6oqvr28dne.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.amazonaws.com%2Fuploads%2Farticles%2F1ikxd0krwg6oqvr28dne.png" alt="Image" width="657" height="147"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The core disagreement, then, is not whether prompts should be part of review. It is where accountability should live in an AI-assisted workflow: primarily in the prompt, primarily in the code, or in a deliberately structured combination of both.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a prompt request?
&lt;/h2&gt;

&lt;p&gt;A prompt request is exactly what it sounds like: a request by a developer for a peer review of their prompt before feeding it into an LLM to generate code. Or, in the case of multi-shot or conversational prompts, a review of the conversation between the developer and the agent.&lt;/p&gt;

&lt;p&gt;Instead of starting review at the diff level, a prompt request asks reviewers to evaluate the instructions given to the LLM so they can sign off on or contribute to the context, intent, constraints, and assumptions that guide the model’s output. A typical prompt request may include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The system and user prompts&lt;/li&gt;
&lt;li&gt;Relevant repository or architectural context&lt;/li&gt;
&lt;li&gt;Model selection and configuration&lt;/li&gt;
&lt;li&gt;Constraints, invariants, or non-goals&lt;/li&gt;
&lt;li&gt;Examples of expected behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is to make explicit what the model was asked to do before evaluating how well it did it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4l3ileilk4wxx4hx5m9f.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.amazonaws.com%2Fuploads%2Farticles%2F4l3ileilk4wxx4hx5m9f.png" alt="Image" width="599" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this sense, a prompt request functions more like a design artifact than a code artifact. It captures intent at the moment of generation and helps ensure the prompt is comprehensive and explicit enough to address the requirements. It can help teams better align around how they prompt and ensure that everyone is using the same context to generate code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Good news: Prompt requests and pull requests are not in conflict
&lt;/h2&gt;

&lt;p&gt;Much of the debate this week stemmed from treating prompt requests and pull requests as competitors. Either you do a prompt request or a pull request, some commenters suggested.&lt;/p&gt;

&lt;p&gt;However, they shouldn’t be.&lt;/p&gt;

&lt;p&gt;After all, they address different failure modes at different stages of the development lifecycle. Just like you’re not going to skip testing because you did a code review, you shouldn’t skip a code review because you did a prompt request.&lt;/p&gt;

&lt;p&gt;Prompt requests are valuable because they ensure alignment and best practices early, before any code is generated or committed. They help teams align on what should be built, define boundaries, and constrain agent behavior. Because large language models are non-deterministic, capturing intent explicitly becomes even more important upstream, where variability is highest.&lt;/p&gt;

&lt;p&gt;A prompt request can also help ensure that a prompt is optimized for the specific model or tool that will be used to generate the code, something essential in ensuring the quality of the output of increasingly divergent models (something we’ve consistently found in our evals).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg2m4esj5et1koq9pkml5.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.amazonaws.com%2Fuploads%2Farticles%2Fg2m4esj5et1koq9pkml5.png" alt="https://github.com/clawdbot/clawdbot/pull/763" width="800" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pull requests remain essential later, when teams review the exact code that will ship. They preserve determinism, traceability, testing, auditing, and accountability. One captures intent. The other captures execution.&lt;/p&gt;

&lt;p&gt;Treating prompt requests as replacements for pull requests creates a false tension. Used together, they complement each other. Doing a prompt request and then skipping a pull request seems reckless and like tempting fate since the actual code produced hasn’t been validated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why teams are drawn to prompt requests
&lt;/h2&gt;

&lt;p&gt;When done as part of the regular software development workflow that includes a thorough code review, prompt requests are a way to shift left and catch issues early. It ensures a team is aligned on the goals of the feature, can help optimize the prompt for the model it’s using, and can ensure that the proper context is being supplied to improve the generated output. This can cut down significantly on review and issues later on.&lt;/p&gt;

&lt;p&gt;When used alone without doing a pull request after the code is generated, the primary appeal of prompt requests is cognitive efficiency and speed.&lt;/p&gt;

&lt;p&gt;AI has dramatically increased the speed at which developers can produce code, but the review process has not kept pace. As AI-authored changes grow larger and more frequent, line-by-line review becomes increasingly difficult and cognitively taxing to complete. Subtle defects slip through not because engineers don’t care, but because reviewing enormous, machine-generated diffs is mentally taxing.&lt;/p&gt;

&lt;p&gt;Prompts, by contrast, are typically shorter and more declarative. Reviewing a prompt allows engineers to reason directly about scope, intent, and constraints without getting buried in implementation details produced by the model.&lt;/p&gt;

&lt;p&gt;Prompt-first review works particularly well for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scaffolding and boilerplate generation&lt;/li&gt;
&lt;li&gt;Small changes&lt;/li&gt;
&lt;li&gt;Greenfield prototypes&lt;/li&gt;
&lt;li&gt;Fast-moving teams optimizing for iteration speed&lt;/li&gt;
&lt;li&gt;Hobby projects where defects in prod aren’t that consequential&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In these cases, the most important question is often not “is every line correct?” but “is this what we meant to build?”&lt;/p&gt;

&lt;h2&gt;
  
  
  Where prompt requests fall short
&lt;/h2&gt;

&lt;p&gt;When used in concert with pull requests, there are few downsides since they simply offer another opportunity to review the proposed code change before generation. The biggest one is the time and cognitive effort it takes and how this could become a new bottleneck for code generation if it takes too long to get a review.&lt;/p&gt;

&lt;p&gt;When treated as a replacement for pull requests, the biggest limitation of prompt requests is non-determinism.&lt;/p&gt;

&lt;p&gt;After all, the same prompt can produce different outputs across runs or models. That makes reviewing prompts a weak substitute for reviewing an auditable record of what actually shipped. From the perspective of git blame, compliance, or legal accountability, prompt reviews alone are insufficient.&lt;/p&gt;

&lt;p&gt;There are also real security and correctness risks. You might think you covered everything in your prompt but it may encode unsafe assumptions, omit edge cases, or fail to account for system-specific constraints that would normally be caught during careful code review. Reviewing intent does not guarantee that the generated output is secure, performant, or compliant.&lt;/p&gt;

&lt;p&gt;Finally, prompts are highly contextual. A prompt that looks reasonable in isolation can still produce problematic implementations if the reviewer lacks deep familiarity with the codebase, infrastructure, or runtime environment. While prompt reviews are designed to limit this by bringing in additional sets of eyes to improve the prompt, human reviewers make mistakes all the time on actual code. Add in the unpredictability of a model and that’s a recipe for bugs and downtime These risks increase as prompts are reused or gradually modified over time or if you change models.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prompt requests work best before pull requests
&lt;/h2&gt;

&lt;p&gt;Used together, prompt requests and pull requests offset each other’s weaknesses.&lt;/p&gt;

&lt;p&gt;A practical workflow might look like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A developer proposes a prompt request describing the intended change, constraints, and assumptions. This can involve just one prompt or a series of prompts for different parts of the code being generated. In the case of conversational prompts, the dev might propose a conversational response or share their transcript with the LLM after the fact. In that case, the review could help reprompt the agent to generate a better result.&lt;/li&gt;
&lt;li&gt;The team reviews and aligns on the prompt(s) before code generation.&lt;/li&gt;
&lt;li&gt;The code is generated and committed.&lt;/li&gt;
&lt;li&gt;A traditional pull request reviews the concrete output for correctness, safety, and fit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this model, prompt requests act as an upstream alignment step for AI-generated work. They reduce ambiguity early, potentially shrink downstream diffs, and make pull requests easier to review.&lt;/p&gt;

&lt;p&gt;Prompt requests do not replace the later rigor needed in pull requests. They just add more rigor earlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Are prompt requests going to replace pull requests?
&lt;/h2&gt;

&lt;p&gt;Let’s be honest, prompt requests are unlikely to fully replace pull requests. No one thinks a large publicly traded company is going to trust AI-generated output so faithfully, they’ll bet their revenue (and future) on it without careful review.&lt;/p&gt;

&lt;p&gt;While we are bullish on prompt requests at CodeRabbit, the industry is still in the early stages of their adoption, and today’s LLMs are not capable of fully replacing pull requests.&lt;/p&gt;

&lt;p&gt;Will prompt requests work instead of pull requests for smaller open-source or single-maintainer projects? We are likely heading toward that reality sooner rather than later, but pull requests remain an essential part of the current software development lifecycle. This is especially true for production systems, regulated environments, or large teams with shared ownership and long-lived, complex codebases.&lt;/p&gt;

&lt;p&gt;Pull requests exist because software development ultimately involves shipping specific, deterministic artifacts into production. As long as that remains true, teams will need a concrete mechanism to review, test, audit, and approve the exact code that runs.&lt;/p&gt;

&lt;p&gt;The more realistic future is not prompt requests instead of pull requests. It is prompt requests before pull requests.&lt;/p&gt;

&lt;p&gt;What is becoming clear is that the quality of the prompt increasingly determines the quality of the output. Treating prompts as first-class artifacts acknowledges that reality without abandoning the safeguards that traditional code review provides.&lt;/p&gt;

&lt;p&gt;In that sense, “show me the prompt” does not remove accountability. It shifts some of it earlier, where it can reduce rework, surface intent, and make the pull request stage easier rather than unnecessary.&lt;/p&gt;

&lt;p&gt;Interested in trying CodeRabbit? Get a &lt;a href="https://coderabbit.link/QjWcnUj" rel="noopener noreferrer"&gt;14-day free trial&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>codereview</category>
      <category>codenewbie</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Why users shouldn’t choose their own LLM models: Choice is not always good</title>
      <dc:creator>Arindam Majumder </dc:creator>
      <pubDate>Thu, 29 Jan 2026 01:29:00 +0000</pubDate>
      <link>https://forem.com/coderabbitai/why-users-shouldnt-choose-their-own-llm-models-choice-is-not-always-good-42nj</link>
      <guid>https://forem.com/coderabbitai/why-users-shouldnt-choose-their-own-llm-models-choice-is-not-always-good-42nj</guid>
      <description>&lt;p&gt;Giving users a dropdown of LLMs to choose from often seems like the right product choice. After all, users might have a favorite model or they might want to try the latest release the moment it drops.&lt;/p&gt;

&lt;p&gt;One problem: unless they’re an ML engineer running regular evals and benchmarks to understand where each model actually performs best, that choice is liable to hurt far more than it helps. You end up giving users what they think they want, while quietly degrading the quality of what they produce with your tool with inconsistent results, wasted tokens, and erratic model behavior.&lt;/p&gt;

&lt;p&gt;For example, developers may unknowingly pick a model that’s slower, less reliable for their specific task, or tuned for a completely different kind of reasoning pattern. Or they might choose a faster model than they need that won’t comprehensively reason through the task.&lt;/p&gt;

&lt;p&gt;Choosing which model to use isn’t a matter of personal taste… It's a systems-level optimization problem. The right model for any task depends on measurable performance across dozens of task dimensions, not just how recently it was released or how smart users perceive it to be. And that decision should belong to engineers armed with eval data, not end users who wrongly believe they’ll get better results with the model they personally prefer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The myth of ‘preference’ in AI model selection
&lt;/h2&gt;

&lt;p&gt;Many AI platforms love to market model choice as a premium feature. “Choose GPT-4o, Claude, or Gemini” sounds empowering and gives users the impression that they will get the best or latest experience. It taps into the same instinct that makes people want to buy the newest phone the week it launches: the feeling that newer and bigger must mean better.&lt;/p&gt;

&lt;p&gt;The reality, though, is that most users have no idea which model actually performs best for their specific use case. And even if they did, that answer would likely shift from one query to another. The “best” model for code generation might not be the “best” for bug detection, documentation, or static analysis. There might also be multiple models that are best at different parts of a code review or other task, depending on what kind of code is being reviewed.&lt;/p&gt;

&lt;p&gt;Some tasks require greater creativity and reasoning depth; others need precision and consistency. A developer who blindly defaults to “the biggest model available” for coding help, often ends up with slower, more expensive, and less deterministic results. In some cases, a smaller, domain-tuned model will handily outperform its heavyweight cousin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why model selection is an evaluation problem, not preference
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft261iarforbza26sdasb.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.amazonaws.com%2Fuploads%2Farticles%2Ft261iarforbza26sdasb.png" alt="Why model selection is an evaluation problem, not preference" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Model selection isn’t a matter of taste… it's a data problem. Behind the scenes, engineers run thousands of evaluations across tasks like code correctness, latency, context retention, and tool integration. These aren’t one-time benchmarks; they’re continuous systems designed to measure how models actually perform under specific, reproducible conditions. The results form a kind of performance map which shows which model excels at refactoring versus summarizing code or which one handles long-context reasoning without drifting off-topic.&lt;/p&gt;

&lt;p&gt;End users never see that map. While some might read benchmarks or articles about a model’s performance, most are making decisions blind, guided mostly by hunches, Reddit posts, or vague impressions of “smartness.”&lt;/p&gt;

&lt;p&gt;Even if they wanted to, users rarely have the time or infrastructure to run their own evals across hundreds of tasks and models. The result is that people often optimize for hype rather than outcomes… choosing the model that feels cleverest or sounds more fluent, not the one that’s objectively better for the job.&lt;/p&gt;

&lt;p&gt;And human perception alone is a terrible way to evaluate model competence. A model that seems chatty and confident can be consistently wrong, while one that feels hesitant might actually deliver the most accurate, reproducible results. Without hard data from evaluations, those distinctions disappear.&lt;/p&gt;

&lt;h2&gt;
  
  
  The prompting paradox
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzukbe2kw524k69prv82v.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.amazonaws.com%2Fuploads%2Farticles%2Fzukbe2kw524k69prv82v.png" alt="The prompting paradox" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One critical drawback to choosing your own model is that no two LLMs think alike. Each model interprets prompts slightly differently. Some are more literal, others more associative; some favor verbosity, others prefer minimalism. A prompt that works perfectly on GPT-5 might completely derail on Sonnet 4.5, leading to hallucinated code, missing context, or an output that ignores key constraints.&lt;/p&gt;

&lt;p&gt;Temperature, context length, and formatting differences only make the problem worse. A model with a higher temperature parameter might produce creative explanations but rewrite variable names, while another with stricter formatting rules could break markdown or indentation. These small mismatches can quietly poison a workflow, especially in environments where consistent structure matters most like with code reviews, diff comments, or documentation summaries.&lt;/p&gt;

&lt;p&gt;When users choose their own models, they unknowingly disrupt the prompt-engineering assumptions that keep those workflows stable in systems where the prompts are written for the user. Every prompt is tuned with certain expectations about how the model parses instructions, handles errors, and formats its output. Swap out the model and those assumptions collapse.&lt;/p&gt;

&lt;p&gt;It’s even harder to navigate in situations where the user writes the prompt themselves, like with AI coding tools. Users rarely have enough context, knowledge, and experience to write effective prompts for each model. However, over time, they might find a few prompting methods that help them get the best out of a particular model. If they later change to a new model, they often find their old prompts aren’t as effective and need to learn from scratch trying to get the best results from that new model.&lt;/p&gt;

&lt;p&gt;That’s why well-designed systems rely on model orchestration, not user preference. In review pipelines or agentic systems, predictability is everything. You need each component to behave consistently so downstream tools and other models can interpret the results. Giving users the freedom to swap models isn’t customization; it’s chaos engineering without the safety net.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hidden costs of model freedom
&lt;/h2&gt;

&lt;p&gt;Once users can switch models at will, all the invisible consistency that makes AI-assisted workflows dependable begins to crumble. The consequences aren’t abstract; they’re measurable and they multiply fast.&lt;/p&gt;

&lt;p&gt;Across teams, the first thing you notice is inconsistency. Two developers can run the same review prompt and get completely different feedback. One gets a precise diff comment, the other might get a philosophical musing on the meaning of clean code. That inconsistency makes it impossible to reproduce results, which is deadly for any process that relies on traceability or QA.&lt;/p&gt;

&lt;p&gt;Then there’s cost. Larger models burn through tokens faster and often respond slower, introducing both financial waste and latency drag. And when users unknowingly pick models with shorter context windows, the result is truncated inputs or missing context. It’s like asking someone to summarize a novel after reading only half of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The better alternative: Dynamic, data-driven routing
&lt;/h2&gt;

&lt;p&gt;The smarter alternative to user-driven chaos is dynamic, data-driven routing. That means systems that automatically choose the right model for the right task. Instead of asking users to guess which LLM might perform best, auto-routing engines make that choice in real-time based on metrics, evals, and historical performance.&lt;/p&gt;

&lt;p&gt;Think of it as orchestration, not selection. A large model might be routed in for creative reasoning, open-ended problem solving, or complex code explanations. A smaller, domain-tuned model might handle deterministic checks, linting, or static analysis where precision and speed matter more than eloquence. The system continuously evaluates the outcomes tracking correctness, latency, and user feedback in order to refine its routing logic over time.&lt;/p&gt;

&lt;p&gt;This approach turns what used to be human guesswork into an adaptive, evidence-based process. The routing system learns which models excel at which tasks, under which conditions, and how to balance cost, speed, and quality.&lt;/p&gt;

&lt;p&gt;Advanced teams already operate this way. In CodeRabbit, for example, the orchestration layer sits between the user and the models, using structured prompts, eval data, and performance histories to dispatch requests intelligently. Developers don’t have to think about which LLM is behind a particular review comment. The system has already chosen the optimal one, validated against internal benchmarks.&lt;/p&gt;

&lt;p&gt;In short, dynamic routing makes model choice invisible. The user gets consistently high-quality results; the engineers get measurable control and efficiency. Everyone wins. Except the dropdown menu.&lt;/p&gt;

&lt;h2&gt;
  
  
  Expertise is in the system, not the slider
&lt;/h2&gt;

&lt;p&gt;The takeaway here is simple: model selection isn’t a feature, it’s a quality control issue. The best results come from systems that make those choices invisibly and are grounded in data, not gut instinct. When model routing is automatic and performance-based, users get consistent, high-quality outputs without needing to think about which model is doing the work.&lt;/p&gt;

&lt;p&gt;Every product that puts a “Choose your LLM” dropdown front and center is outsourcing an engineering decision to the least equipped person to make it.&lt;/p&gt;

&lt;p&gt;Or, put another way: the best AI tool UI is no LLM dropdown at all.&lt;/p&gt;

&lt;p&gt;Curious what it looks like when an AI pipeline optimizes for LLM fit? Try CodeRabbit for free today!&lt;/p&gt;

</description>
      <category>codereview</category>
      <category>productivity</category>
      <category>llm</category>
      <category>programming</category>
    </item>
    <item>
      <title>An (actually useful) framework for evaluating AI code review tools</title>
      <dc:creator>Arindam Majumder </dc:creator>
      <pubDate>Wed, 28 Jan 2026 12:24:44 +0000</pubDate>
      <link>https://forem.com/coderabbitai/an-actually-useful-framework-for-evaluating-ai-code-review-tools-1g2p</link>
      <guid>https://forem.com/coderabbitai/an-actually-useful-framework-for-evaluating-ai-code-review-tools-1g2p</guid>
      <description>&lt;p&gt;Benchmarks promise clarity. They’re supposed to reduce a complex system to a score, compare competitors side by side, and let the numbers speak for themselves. But, in practice, they rarely do.&lt;/p&gt;

&lt;p&gt;Benchmarks don’t measure “quality” in the abstract. They measure whatever the benchmark designer chose to emphasize, under the specific constraints, assumptions, and incentives of the evaluation.&lt;/p&gt;

&lt;p&gt;Change the dataset, the scoring rubric, the prompts, or the evaluation harness, and the results can shift dramatically. That doesn’t make benchmarks useless, but it does make them fragile, manipulable, and easy to misinterpret. Case in point: database benchmarks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Database benchmarks: A cautionary tale
&lt;/h2&gt;

&lt;p&gt;The history of database performance benchmarks is a useful example. As benchmarks became standardized, vendors learned how to optimize specifically for the test rather than for real workloads. Query plans were hand-tuned, caching behavior was engineered to exploit assumptions, and systems were configured in ways no production team would realistically deploy.&lt;/p&gt;

&lt;p&gt;Over time, many engineers stopped trusting benchmark results, treating them as marketing signals rather than reliable indicators of system behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI code review benchmarks are on the same trajectory
&lt;/h2&gt;

&lt;p&gt;We’re currently seeing AI code review benchmarks go down a similar path. As models are evaluated on curated PR sets, synthetic issues, or narrowly defined correctness criteria, tools increasingly optimize for benchmark performance rather than for the messy, contextual, high‑stakes reality of real code review.&lt;/p&gt;

&lt;p&gt;The deeper problem is not just that benchmarks can be misleading, it’s that many “ideal” evaluation designs are difficult to execute correctly in real engineering environments. When an evaluation framework is too detached from real workflows, too easy to game by badly configuring your competitor’s tool, or too complex to run well, the results become hard to trust.&lt;/p&gt;

&lt;p&gt;What follows below is a practical framework for effectively evaluating AI code review tools that balances rigor with feasibility, and produces results that are both meaningful and interpretable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start from your objectives and make them explicit
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3q4qi11rxkv1yzml2uvm.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.amazonaws.com%2Fuploads%2Farticles%2F3q4qi11rxkv1yzml2uvm.png" alt="Image1" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before assembling datasets or choosing metrics, it’s critical to define what you actually care about. “Better code review” means different things to different teams, and an evaluation that doesn’t encode those differences will inevitably optimize for the wrong outcome.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Catching real defects and risks before merge&lt;/li&gt;
&lt;li&gt;Improving long‑term maintainability and reducing technical debt&lt;/li&gt;
&lt;li&gt;Avoiding low‑value noise that degrades review quality&lt;/li&gt;
&lt;li&gt;Maintaining developer trust and adoption&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s also important to distinguish between leading indicators and lagging indicators. Outcomes like fewer production incidents or higher long‑term throughput are real and important, but they often emerge over months, not weeks. Shorter evaluations should focus on signals that correlate strongly with those outcomes, such as the quality of issues caught, whether they are acted on, and how developers respond to the tool.&lt;/p&gt;

&lt;p&gt;Explicitly ranking your objectives such as quality impact, precision, developer experience, and throughput, helps ensure that your evaluation answers the questions that actually matter to your organization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Determine what kind of evaluation is needed
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feiarq8bw6kgr24siskjo.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.amazonaws.com%2Fuploads%2Farticles%2Feiarq8bw6kgr24siskjo.png" alt="Image2" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The most reliable evaluation of any tool involves a real-world pilot over a controlled offline benchmark. This allows you to see how it works in day-to-day situations versus just evaluating a tool based on criteria defined by a third party vendor.&lt;/p&gt;

&lt;h3&gt;
  
  
  In-the-wild pilot
&lt;/h3&gt;

&lt;p&gt;The most reliable signals come from observing how a tool behaves in real, day‑to‑day development.&lt;/p&gt;

&lt;p&gt;Real‑time evaluation reflects actual constraints: deadlines, partial context, competing priorities, and human judgment. It shows not just what a tool can detect in theory, but what it surfaces in practice, and whether those issues matter enough for developers to act on them.&lt;/p&gt;

&lt;p&gt;For this, select a few teams or projects for each tool and run each tool for a period of time under normal usage.&lt;/p&gt;

&lt;p&gt;Measure things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-world detection of issues.&lt;/li&gt;
&lt;li&gt;Severity of issues caught.&lt;/li&gt;
&lt;li&gt;Developer satisfaction and perceived utility.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If possible, design A/B style experiments so you can measure using the tool vs no tool on comparable teams or repos or Tool A vs Tool B on similar workloads, perhaps alternating weeks or branches.&lt;/p&gt;

&lt;h3&gt;
  
  
  Offline benchmark
&lt;/h3&gt;

&lt;p&gt;For teams that want additional confidence, controlled detection comparisons can provide useful insight if you design it yourself using your own pull requests and criteria so it gives you the data you actually need. However, it’s not required in most cases since it doesn’t provide as much useful data as a pilot and can be time intensive to set up.&lt;/p&gt;

&lt;p&gt;One practical approach is to use a private evaluation or mirror repository. A small, representative set of pull requests can be replayed, allowing multiple tools to be run on the same diffs without disrupting real workflows.&lt;/p&gt;

&lt;p&gt;These comparisons are best used to understand coverage differences by severity and category, and to identify systematic strengths and blind spots across tools.&lt;/p&gt;

&lt;p&gt;After that, you just need to compute the metrics you’re looking to track. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Precision/recall by severity and issue type.&lt;/li&gt;
&lt;li&gt;Comment volume and distribution.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why evaluating multiple tools on the same pull request is usually misleading
&lt;/h2&gt;

&lt;p&gt;If you want to do a head-to-head comparison via either a benchmark or a pilot, a common instinct is to run them all on the same exact pull requests rather than mirroring that PR and running each tool you’re comparing separately on it or running them on different but comparable PRs. On the surface, running them all simultaneously feels fair and efficient. In practice, it introduces serious problems.&lt;/p&gt;

&lt;p&gt;When multiple AI reviewers comment on the same PR:&lt;/p&gt;

&lt;p&gt;Human reviewers are overwhelmed with feedback and cognitive load spikes.&lt;/p&gt;

&lt;p&gt;No single tool can be experienced as it was designed to be used in that case. For example, some tools skip comments if they see another tool has already made that comment leading to the perception that that tool hasn’t found the issue.&lt;/p&gt;

&lt;p&gt;Review behavior changes—comments are skimmed, bulk‑dismissed, or ignored&lt;/p&gt;

&lt;p&gt;This creates interference effects. Tools influence each other’s perceived usefulness, and attention, not correctness, becomes the limiting factor. Precision metrics degrade because even high‑quality comments may be ignored simply due to volume. That makes it harder to know the percentage of comments your team would accept from each individual tool under normal usage.&lt;/p&gt;

&lt;p&gt;The result is that you lose the ability to evaluate usability, trust, workflow fit, and real‑world usefulness. You are no longer measuring how a tool performs in practice, but how reviewers cope with noise.&lt;/p&gt;

&lt;p&gt;Running multiple tools on the same exact PR can be useful in narrow, controlled contexts, such as offline detection comparisons, but it is a poor way to evaluate the actual experience and value of a code review tool.&lt;/p&gt;

&lt;p&gt;To understand whether a tool helps your team, it often best be experienced in isolation within a normal review workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structuring fair comparisons without complex infrastructure
&lt;/h2&gt;

&lt;p&gt;There are practical ways to compare tools without building elaborate experimentation harnesses.&lt;/p&gt;

&lt;p&gt;Parallel evaluation across repos or teams is often the simplest approach. Select repos or teams that are broadly comparable in language, domain, and PR volume, and run different tools in parallel. Keep configuration effort symmetric and analyze results using normalization techniques (discussed below).&lt;/p&gt;

&lt;p&gt;Alternatively, time‑sliced evaluation within the same repo or team can work when parallel groups are not available. Run one tool for a defined period, then switch. This approach requires acknowledging temporal effects—release cycles, workload changes, learning effects—but can still produce useful, directional insights when interpreted carefully.&lt;/p&gt;

&lt;p&gt;Finally, simply mirroring PRs and running reviews on them with separate tools also works well, if you want to compare comments on the same PRs.&lt;/p&gt;

&lt;p&gt;In all these cases, the goal is to preserve a clean developer experience while collecting comparable data.&lt;/p&gt;

&lt;p&gt;In practice, these approaches can also be combined if a team feels like that’s helpful to give them a better idea of how a tool works. Teams may start with parallel evaluation across different repositories or teams, then swap tools after a fixed period. This helps balance differences in codebase complexity or workload over time, while still avoiding the disruption and interference that comes from running multiple tools on the same pull request. As with any time-based comparison, results should be normalized and interpreted with awareness of temporal effects, but this hybrid approach often provides a good balance of fairness, practicality, and interpretability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Metrics that produce interpretable results
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fynm7z2hfre0jsakkpkc5.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.amazonaws.com%2Fuploads%2Farticles%2Fynm7z2hfre0jsakkpkc5.png" alt="Image4" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Based on successful deployments across thousands of repositories, we've identified a framework of seven metric categories that provide a complete picture of your integration which we suggest as metrics to measure to our customers.&lt;/p&gt;

&lt;p&gt;Each category answers a specific question about your AI implementation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Architectural Metrics – Is the tool appropriately integrated? How many of an org’s repos are connected, how many extensions are they using (git, IDE, CLI).&lt;/li&gt;
&lt;li&gt;Adoption Metrics – Are developers actually using it? These metrics include monthly active users (MAU), the percentage of total repositories covered and week-over-week growth.&lt;/li&gt;
&lt;li&gt;Engagement Metrics – Are they just ignoring it or actively collaborating with it? These metrics include PRs reviewed versus Chat Sessions initiated. Also track “Learnings used,” how often the AI applies context from previous reviews to new ones.&lt;/li&gt;
&lt;li&gt;Impact Metrics – Is it catching bugs that matter to the team? These metrics include number of issues detected, actionable suggestions, and the “acceptance rate” (percentage of AI comments that result in a code change).&lt;/li&gt;
&lt;li&gt;Quality &amp;amp; Security Metrics – Is it preventing expensive bugs and security vulnerabilities? These metrics include Linter/SAST findings, security vulnerabilities caught (e.g., Gitleaks), and reduction in pipeline failures.&lt;/li&gt;
&lt;li&gt;Governance Metrics – Is it enforcing standards across the team? These metrics include usage of pre-merge checks, warnings vs. errors, and implementation of custom governance rules.&lt;/li&gt;
&lt;li&gt;Developer Sentiment – Are the developers happy with their experience and product? These metrics include survey results, qualitative feedback, and “aha” moments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Accepted issues as a primary quality signal
&lt;/h3&gt;

&lt;p&gt;Not all metrics are equally informative and some are far easier to misread than others. A practical evaluation should focus more attention on signals that are both meaningful and feasible to measure. One of the strongest indicators of value is whether a tool’s feedback leads to real action.&lt;/p&gt;

&lt;p&gt;An issue can reasonably be considered accepted when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A subsequent commit addresses the comment or thread&lt;/li&gt;
&lt;li&gt;A reviewer explicitly acknowledges that the issue has been resolved&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This behavioral signal captures correctness, relevance, and usefulness in a way that pure scoring metrics cannot.&lt;/p&gt;

&lt;p&gt;Accepted issues should be reported by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Severity (e.g., critical, major, minor, low, nitpick)&lt;/li&gt;
&lt;li&gt;Category (security, logic, performance, maintainability, testing, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both absolute counts and rates are informative, especially when interpreted together.&lt;/p&gt;

&lt;h3&gt;
  
  
  Precision and signal‑to‑noise
&lt;/h3&gt;

&lt;p&gt;Acceptance rate (accepted issues relative to total surfaced) is a practical proxy for precision. On its own, it is insufficient; paired with comment volume, it becomes far more meaningful.&lt;/p&gt;

&lt;p&gt;High comment volume with low acceptance is a clear signal of noise. Patterns of systematically ignored categories or directories often reveal where configuration or tuning is needed.&lt;/p&gt;

&lt;p&gt;It’s also important to avoid the “LGTM trap.” That means a tool that leaves very few comments, all correct, may appear precise while missing large classes of issues. In many cases, broad coverage combined with configurability is preferable to narrow precision that cannot be expanded.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coverage and issue discovery in real review flows
&lt;/h3&gt;

&lt;p&gt;In typical workflows, the sequence is:&lt;/p&gt;

&lt;p&gt;PR opens → AI review → issues fixed → human review&lt;/p&gt;

&lt;p&gt;Because humans review after the tool, it is often impossible to say with certainty which issues humans would have caught independently. Instead of trying to infer counterfactuals precisely, focus on practical signals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accepted issues that led to substantive code changes&lt;/li&gt;
&lt;li&gt;Accepted issues in categories humans historically miss (subtle logic, edge cases, maintainability)&lt;/li&gt;
&lt;li&gt;Consistent patterns of issues surfaced across PRs
Sampling can help here. Reviewing a subset of PRs and asking, “Would this issue likely have been caught without the tool?” is often more informative than attempting exhaustive labeling.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Normalization: Making comparisons fair
&lt;/h3&gt;

&lt;p&gt;Raw counts are misleading when pull requests vary widely in size and complexity. Normalization is essential for fair comparison.&lt;/p&gt;

&lt;p&gt;Useful normalization dimensions include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PR size (lines changed, files touched)&lt;/li&gt;
&lt;li&gt;PR type (bug fix, feature, refactor, infra/config, test‑only)&lt;/li&gt;
&lt;li&gt;Domain or risk area (frontend/backend, high‑risk components)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Comparisons should be made within similar buckets, and distributions are often more informative than averages. Small samples at extremes should be interpreted cautiously.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interpreting throughput and velocity
&lt;/h3&gt;

&lt;p&gt;Throughput metrics like time‑to‑merge are easy to misread. When a tool begins catching real issues that were previously missed, merge times may initially increase. This often reflects improved rigor rather than reduced productivity.&lt;/p&gt;

&lt;p&gt;Throughput should therefore be treated as a secondary metric, normalized by PR complexity and evaluated over time alongside quality indicators. Short‑term slowdowns can be a leading indicator of long‑term gains in code health.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bringing it all together
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4ms6ekv9g2ofxqnzg6h.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.amazonaws.com%2Fuploads%2Farticles%2Fm4ms6ekv9g2ofxqnzg6h.png" alt="Image4" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A reliable evaluation does not require perfect benchmarks or elaborate experimental design. It requires clarity about objectives, careful interpretation of metrics, and an emphasis on real‑world behavior.&lt;/p&gt;

&lt;p&gt;Start with normal workflows and behavioral signals. Normalize to make comparisons fair. Use controlled comparisons selectively to deepen understanding. Combine quantitative metrics with concrete examples of impact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;Benchmarks are useful starting points, not verdicts.&lt;/p&gt;

&lt;p&gt;The most trustworthy evaluations of AI code review tools are grounded in real workflows, user behavior‑based signals, and balance rigor with practicality. When done well, they provide confidence not just that a tool performs well on paper, but that it meaningfully improves both the immediate quality of code changes and the long‑term health of the codebase.&lt;/p&gt;

&lt;p&gt;Curious how CodeRabbit performs on your codebase? Get a &lt;a href="https://coderabbit.link/QjWcnUj" rel="noopener noreferrer"&gt;free trial today&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>codereview</category>
      <category>benchmark</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>CodeRabbit's AI Code Reviews now support NVIDIA Nemotron</title>
      <dc:creator>Arindam Majumder </dc:creator>
      <pubDate>Wed, 28 Jan 2026 12:17:13 +0000</pubDate>
      <link>https://forem.com/coderabbitai/coderabbits-ai-code-reviews-now-support-nvidia-nemotron-27pn</link>
      <guid>https://forem.com/coderabbitai/coderabbits-ai-code-reviews-now-support-nvidia-nemotron-27pn</guid>
      <description>&lt;p&gt;TL;DR: Blend of frontier &amp;amp; open models is more cost efficient and reviews faster. NVIDIA Nemotron is supported for CodeRabbit self-hosted customers.&lt;/p&gt;

&lt;p&gt;We are delighted to share that CodeRabbit now supports the NVIDIA Nemotron family of open models among its blend of Large Language Models (LLMs) used for AI code reviews. Support for Nemotron 3 Nano has initially been enabled for CodeRabbit’s self-hosted customers running its container image on their infrastructure. Nemotron is used to power the context gathering and summarization stage of the code review workflow before the frontier models from OpenAI and Anthropic are used for deep reasoning and generating review comments for bug fixes.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Nemotron helps: Context gathering at scale
&lt;/h2&gt;

&lt;p&gt;This new blend of open and frontier models allows us to improve the overall speed of context gathering and improves cost efficiency by routing different parts of the review workflow to the appropriate model family, while delivering review accuracy that is at par with running frontier models alone.&lt;/p&gt;

&lt;p&gt;High quality AI code reviews that can find deep lying and hidden bugs require lots of context gathering related to the code being analyzed. The most frequent (and most token-hungry) work is summarizing and refreshing that context: what changed in the code and does it match developer intent, how do those changes connect with rest of the codebase, what are the repo conventions or custom rules, what external data sources are available to aid the review, etc.&lt;/p&gt;

&lt;p&gt;This context building stage is the workhorse of the overall AI code review process and it is run several times iteratively throughout the review workflow. &lt;a href="https://huggingface.co/nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16" rel="noopener noreferrer"&gt;NVIDIA Nemotron 3 Nano&lt;/a&gt; was built for high-efficiency tasks and its large context window (1 million tokens) along with fast speed helps to gather a lot of data and run several iterations of context summarization and retrieval.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1767647146546%2F906c3149-a404-471e-ae4e-1c146ccedb7f.png%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" 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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1767647146546%2F906c3149-a404-471e-ae4e-1c146ccedb7f.png%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" alt="CodeRabbit architecture with Nemotron support" width="720" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Blend of frontier and Open Models
&lt;/h2&gt;

&lt;p&gt;When you open a Pull Request (PR), CodeRabbit’s code review workflow is triggered starting with an isolated and secure sandbox environment where CodeRabbit analyzes code from a clone of the repo. In parallel, CodeRabbit pulls in context signals from several sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code and PR index&lt;/li&gt;
&lt;li&gt;Linter / Static App Security Tests (SAST)&lt;/li&gt;
&lt;li&gt;Code graph&lt;/li&gt;
&lt;li&gt;Coding agent rules files&lt;/li&gt;
&lt;li&gt;Custom review rules and Learnings&lt;/li&gt;
&lt;li&gt;Issue tickets (Jira, Linear, Github issues)&lt;/li&gt;
&lt;li&gt;Public MCP servers&lt;/li&gt;
&lt;li&gt;Web search&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To dive deeper into our context engineering approach you can check out our blog: &lt;a href="https://www.coderabbit.ai/blog/the-art-and-science-of-context-engineering" rel="noopener noreferrer"&gt;The art and science of context engineering for AI code reviews&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A lot of this context, along with the code diff being analyzed, is used to generate a PR Summary before any review comments are generated. This is where open models come in. Instead of sending all of the context to frontier models, CodeRabbit now uses Nemotron Nano v3 to gather and summarize the relevant context. Summarization is at the heart of every code review and is the key to delivering high signal-to-noise in the review comments.&lt;/p&gt;

&lt;p&gt;After the summarization stage is completed the frontier models (e.g., OpenAI GPT-5.2-Codex and Anthropic Claude-Opus/Sonnet 4.5) perform deep reasoning to generate review comments for bug fixes, and execute agentic steps like review verification, pre-merge checks, and “finishing touches” (including docstrings and unit test suggestions).&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for our customers
&lt;/h2&gt;

&lt;p&gt;CodeRabbit is now enabling Nemotron-3-Nano-30B support (initially for its self-hosted customers) for the context summarization part of the review workflow along with the frontier models from OpenAI and Anthropic. This results in faster code reviews without compromising quality.&lt;/p&gt;

&lt;p&gt;We are also delighted to support the &lt;a href="https://blogs.nvidia.com/blog/open-models-data-tools-accelerate-ai" rel="noopener noreferrer"&gt;announcement from NVIDIA&lt;/a&gt; today about the expansion of its Nemotron family of open models and are excited to work with the company to help accelerate AI coding adoption across every industry.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.coderabbit.ai/contact-us/sales" rel="noopener noreferrer"&gt;Get in touch&lt;/a&gt; with our team to access CodeRabbit’s container image if you would like to run AI code reviews on your self-hosted infrastructure.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>What's New in CodeRabbit: January 2026 Edition</title>
      <dc:creator>Arindam Majumder </dc:creator>
      <pubDate>Wed, 28 Jan 2026 08:17:14 +0000</pubDate>
      <link>https://forem.com/coderabbitai/whats-new-in-coderabbit-january-2026-edition-13dk</link>
      <guid>https://forem.com/coderabbitai/whats-new-in-coderabbit-january-2026-edition-13dk</guid>
      <description>&lt;p&gt;January kicked off strong with powerful new APIs for user and metrics management, plus streamlined data export capabilities.&lt;/p&gt;

&lt;p&gt;Alongside these product updates, CodeRabbit just won a 2026 DEVIES Award at &lt;a href="https://x.com/DeveloperWeek" rel="noopener noreferrer"&gt;DeveloperWeek&lt;/a&gt;! We're honored to be recognized alongside the best in dev tools for helping teams ship cleaner code, faster.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1k88xnx81f3gh6pzpl1k.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.amazonaws.com%2Fuploads%2Farticles%2F1k88xnx81f3gh6pzpl1k.png" alt="Coderabbit DEVIES Award" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We're also excited to announce that CodeRabbit's AI code reviews now support NVIDIA Nemotron open models! Self-hosted teams get faster, more cost-efficient reviews without sacrificing quality.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-2008303896863928826-670" src="https://platform.twitter.com/embed/Tweet.html?id=2008303896863928826"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-2008303896863928826-670');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=2008303896863928826&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;With that context, here's everything we shipped in January 2026:&lt;/p&gt;

&lt;h2&gt;
  
  
  User Management API (Jan 21)
&lt;/h2&gt;

&lt;p&gt;Managing user seats and roles at scale is now possible through our REST API. Whether you're onboarding a new team or adjusting access across departments, you can now automate the entire process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1oatncr7ksazv5j7ate4.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.amazonaws.com%2Fuploads%2Farticles%2F1oatncr7ksazv5j7ate4.png" alt="coderabbit unser manageement api" width="800" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you can do:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List all users in your organization with filters for seat status and role&lt;/li&gt;
&lt;li&gt;Bulk assign or unassign up to 500 seats per request&lt;/li&gt;
&lt;li&gt;Promote or demote users between admin and member roles in bulk&lt;/li&gt;
&lt;li&gt;Get detailed success/failure feedback for each operation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All endpoints support partial success; if one user operation fails, the rest still complete.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.coderabbit.ai/api-reference/users-list" rel="noopener noreferrer"&gt;See the User Management API documentation&lt;/a&gt; for authentication and usage details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Export (Jan 14)
&lt;/h2&gt;

&lt;p&gt;Export your pull request review metrics directly from the CodeRabbit Dashboard. Pick your date range, and download a CSV with complexity scores, review times, and comment breakdowns by severity and category.&lt;/p&gt;

&lt;p&gt;Perfect for quarterly reviews, team retrospectives, or building custom analytics dashboards. Access it from the Data Export tab in your dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.coderabbit.ai/guides/data-export" rel="noopener noreferrer"&gt;See the Data Export documentation&lt;/a&gt; for the complete list of fields.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review Metrics API (Jan 14)
&lt;/h2&gt;

&lt;p&gt;Need programmatic access to your review metrics? The new REST API gives you the same data as the dashboard export, but with full query flexibility. Filter by repository, user, or custom date ranges, and get responses in JSON or CSV format.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftqddjfefffrk3vn5jdgd.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.amazonaws.com%2Fuploads%2Farticles%2Ftqddjfefffrk3vn5jdgd.png" alt="CodeRabbit metrics api" width="800" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you can do:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Query metrics for any date range programmatically&lt;/li&gt;
&lt;li&gt;Filter results by specific repositories or users&lt;/li&gt;
&lt;li&gt;Choose JSON for integration or CSV for analysis&lt;/li&gt;
&lt;li&gt;Build custom reporting dashboards and automation workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://docs.coderabbit.ai/api-reference/metrics-data-api" rel="noopener noreferrer"&gt;See the Metrics API documentation&lt;/a&gt; for authentication and usage details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stay Tuned
&lt;/h2&gt;

&lt;p&gt;We’re continually working to make CodeRabbit smarter, faster, and more collaborative. More updates are on the way, stay tuned!&lt;/p&gt;

&lt;p&gt;Got feedback or want early access to what’s next?&lt;/p&gt;

&lt;p&gt;Join us on &lt;a href="https://discord.gg/coderabbit" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; or follow &lt;a href="https://x.com/coderabbitai" rel="noopener noreferrer"&gt;@coderabbitai on X&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>webdev</category>
      <category>programming</category>
      <category>ai</category>
    </item>
    <item>
      <title>Our new report: AI code creates 1.7x more problems</title>
      <dc:creator>Arindam Majumder </dc:creator>
      <pubDate>Tue, 06 Jan 2026 09:53:09 +0000</pubDate>
      <link>https://forem.com/coderabbitai/our-new-report-ai-code-creates-17x-more-problems-7lh</link>
      <guid>https://forem.com/coderabbitai/our-new-report-ai-code-creates-17x-more-problems-7lh</guid>
      <description>&lt;p&gt;What we learned from analyzing hundreds of open-source pull requests.&lt;/p&gt;

&lt;p&gt;Over the past year, AI coding assistants have gone from emerging tools to everyday fixtures in the development workflow. At many organizations, a part of every code change is now machine-generated or machine-assisted.&lt;/p&gt;

&lt;p&gt;But while this has been accelerating the speed of development, questions have been quietly circulating:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why are more defects slipping through into staging?&lt;/li&gt;
&lt;li&gt;Why do certain logic or configuration issues keep appearing?&lt;/li&gt;
&lt;li&gt;And are these patterns tied to AI-generated code?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It would appear like AI is playing a significant role. A &lt;a href="https://go.cortex.io/rs/563-WJM-722/images/2026-Benchmark-Report.pdf?version=0" rel="noopener noreferrer"&gt;recent report&lt;/a&gt; found that while pull requests per author increased by 20% year-over-year, thanks to help from AI, incidents per pull request increased by 23.5%.&lt;/p&gt;

&lt;p&gt;This year also brought several high-visibility incidents, postmortems, and anecdotal stories pointing to AI-written changes as a contributing factor. These weren’t fringe cases or misuses. They involved otherwise normal pull requests that simply embedded subtle mistakes. And yet, despite rapid adoption of AI coding tools, there has been surprisingly little concrete data about how AI-authored PRs differ in quality from human-written ones.&lt;/p&gt;

&lt;p&gt;So, CodeRabbit set out to answer that question empirically in our &lt;a href="http://www.coderabbit.ai/whitepapers/state-of-AI-vs-human-code-generation-report?" rel="noopener noreferrer"&gt;State of AI vs Human Code Generation Report&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our State of AI vs Human Code Generation Report
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsqz37icfk4j2p28u2ku4.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.amazonaws.com%2Fuploads%2Farticles%2Fsqz37icfk4j2p28u2ku4.png" alt=" " width="800" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We analyzed 470 open-source GitHub pull requests, including 320 AI-co-authored PRs and 150 human-only PRs, using CodeRabbit’s structured issue taxonomy. Every finding was normalized to issues per 100 PRs and we used statistical rate ratios to compare how often different types of problems appeared in each group.&lt;/p&gt;

&lt;p&gt;The results? Clear, measurable, and consistent with what many developers have been feeling intuitively: AI accelerates output, but it also amplifies certain categories of mistakes.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.coderabbit.ai/whitepapers/state-of-AI-vs-human-code-generation-report?" rel="noopener noreferrer"&gt;READ THE FULL REPORT&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations of our study
&lt;/h2&gt;

&lt;p&gt;Getting data on issues that are more prevalent in AI-authored PRs is critical for engineering teams but the challenge was determining which PRs were AI-authored vs human authored. Since it was impossible to directly confirm authorship of each PR of a large enough OSS dataset, we checked for signals that a PR was co-authored by AI and assumed that those that didn’t have it were human authored, for the purposes of the study.&lt;/p&gt;

&lt;p&gt;This resulted in statistically significant differences in issue patterns between the two datasets, which we are sharing in this study so teams can better know what to look for. However, we cannot guarantee all the PRs we labelled as human authored were actually authored only by humans. Our full methodology is shared at the end of the report.&lt;/p&gt;

&lt;h2&gt;
  
  
  Top 10 findings from the report
&lt;/h2&gt;

&lt;p&gt;No issue category was uniquely AI but most categories saw significantly more errors in AI-authored PRs. That means, humans and AI make the same kinds of mistakes. AI just makes many of them more often and at a larger scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. AI-generated PRs contained ~1.7× more issues overall.
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa5vzzm4c705q825c9rz8.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.amazonaws.com%2Fuploads%2Farticles%2Fa5vzzm4c705q825c9rz8.png" alt="Image" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Across 470 PRs, AI-authored changes produced 10.83 issues per PR, compared to 6.45 for human-only PRs. Even more striking: high-issue outliers were much more common in AI PRs, creating heavy review workloads.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Severity escalates with AI: More critical and major issues.
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ocaswl32qtmc5xbpar6.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.amazonaws.com%2Fuploads%2Farticles%2F0ocaswl32qtmc5xbpar6.png" alt="Image" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AI PRs show ~1.4–1.7× more critical and major findings.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Logic and correctness issues were 75% more common in AI PRs.
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Futg7615yyexr10zvid62.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.amazonaws.com%2Fuploads%2Farticles%2Futg7615yyexr10zvid62.png" alt="Image" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These include business logic mistakes, incorrect dependencies, flawed control flow, and misconfigurations. Logic errors are among the most expensive to fix and most likely to cause downstream incidents.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Readability issues spiked more than 3× in AI contributions.
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzsebmz9extdwxjogc2be.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.amazonaws.com%2Fuploads%2Farticles%2Fzsebmz9extdwxjogc2be.png" alt="Image" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The single biggest difference across the entire dataset was in readability. AI-produced code often looks consistent but violates local patterns around naming, clarity, and structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Error handling and exception-path gaps were nearly 2× more common.
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc41n57udzve5x2hjrty8.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.amazonaws.com%2Fuploads%2Farticles%2Fc41n57udzve5x2hjrty8.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AI-generated code often omits null checks, early returns, guardrails, and comprehensive exception logic, issues tightly tied to real-world outages.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Security issues were up to 2.74× higher
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbwahodeu88fplg2z9maa.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.amazonaws.com%2Fuploads%2Farticles%2Fbwahodeu88fplg2z9maa.png" alt="Image" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The most prominent pattern involved improper password handling and insecure object references. While no vulnerability type was unique to AI, nearly all were amplified.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Performance regressions, though small in number, skewed heavily toward AI.
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fidvzd2ek8m8wju03re5n.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.amazonaws.com%2Fuploads%2Farticles%2Fidvzd2ek8m8wju03re5n.png" alt="Image" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Excessive I/O operations were ~8× more common in AI-authored PRs. This reflects AI’s tendency to favor clarity and simple patterns over resource efficiency.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Concurrency and dependency correctness saw ~2× increases.
&lt;/h3&gt;

&lt;p&gt;Incorrect ordering, faulty dependency flow, or misuse of concurrency primitives appeared far more frequently in AI PRs. These were small mistakes with big implication&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Formatting problems were 2.66× more common in AI PRs.
&lt;/h3&gt;

&lt;p&gt;Even teams with formatters and linters saw elevated noise: spacing, indentation, structural inconsistencies, and style drift were all more prevalent in AI-generated code.&lt;/p&gt;

&lt;h3&gt;
  
  
  10. AI introduced nearly 2× more naming inconsistencies.
&lt;/h3&gt;

&lt;p&gt;Unclear naming, mismatched terminology, and generic identifiers appeared frequently in AI-generated changes, increasing cognitive load for reviewers.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.coderabbit.ai/whitepapers/state-of-AI-vs-human-code-generation-report?" rel="noopener noreferrer"&gt;READ THE FULL REPORT&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why these patterns appear
&lt;/h2&gt;

&lt;p&gt;Why are teams seeing so many issues with AI-generated code? Here’s our analysis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI lacks local business logic: Models infer code patterns statistically, not semantically. Without strict constraints, they miss the rules of the system that senior engineers internalize.&lt;/li&gt;
&lt;li&gt;AI generates surface-level correctness: It produces code that looks right but may skip control-flow protections or misuse dependency ordering.&lt;/li&gt;
&lt;li&gt;AI doesn’t adhere perfectly to repo idioms: Naming patterns, architectural norms, and formatting conventions often drift toward generic defaults.&lt;/li&gt;
&lt;li&gt;Security patterns degrade without explicit prompts: Unless guarded, models recreate legacy patterns or outdated practices found in older training data.&lt;/li&gt;
&lt;li&gt;AI favors clarity over efficiency: Models often default to simple loops, repeated I/O, or unoptimized data structures.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What engineering teams can do about it
&lt;/h2&gt;

&lt;p&gt;Adopting AI coding tools isn’t simply about speeding up development. It requires rethinking the guardrails that ensure all code entering production is safe, maintainable, and correct.&lt;/p&gt;

&lt;p&gt;Based on the patterns in the data, here are the most important takeaways for teams:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Give AI the context it needs
&lt;/h3&gt;

&lt;p&gt;AI makes more mistakes when it lacks business rules, configuration patterns, or architectural constraints. Provide prompt snippets, repo-specific instruction capsules, and configuration schemas to reduce misconfigurations and logic drift.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Use policy-as-code to enforce style
&lt;/h3&gt;

&lt;p&gt;Readability and formatting were some of the biggest gaps. CI-enforced formatters, linters, and style guides eliminate entire categories of AI-driven issues before review.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Add correctness safety rails
&lt;/h3&gt;

&lt;p&gt;Given the rise in logic and error-handling issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Require tests for non-trivial control flow&lt;/li&gt;
&lt;li&gt;Mandate nullability/type assertions&lt;/li&gt;
&lt;li&gt;Standardize exception-handling rules&lt;/li&gt;
&lt;li&gt;Explicitly prompt for guardrails where needed&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Strengthen security defaults
&lt;/h3&gt;

&lt;p&gt;Mitigate elevated vulnerability rates by centralizing credential handling, blocking ad-hoc password usage, and running SAST and security linters automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Nudge the model toward efficient patterns
&lt;/h3&gt;

&lt;p&gt;Offer guidelines for batching I/O, choosing appropriate data structures, and using performance hints in prompts.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Adopt AI-aware PR checklists
&lt;/h3&gt;

&lt;p&gt;Reviewers should explicitly ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are error paths covered?&lt;/li&gt;
&lt;li&gt;Are concurrency primitives correct?&lt;/li&gt;
&lt;li&gt;Are configuration values validated?&lt;/li&gt;
&lt;li&gt;Are passwords handled via the approved helper?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These questions target the areas where AI is most error-prone.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Get help reviewing and testing AI code
&lt;/h3&gt;

&lt;p&gt;Code review pipelines weren’t created to handle the higher volume of code teams are currently shipping with the help of AI. Reviewer fatigue has been &lt;a href="https://smartbear.com/resources/case-studies/cisco-systems-collaborator/" rel="noopener noreferrer"&gt;found to lead to more issues&lt;/a&gt; and missed bugs. An AI code review tool like CodeRabbit helps by standardizing code reviews acts as a third-party source of truth that standardizes quality across different AI tools that teams might use while reducing the time and cognitive labor needed for reviews. That allows developers to concentrate on reviewing the more complex parts of the code changes and reduce the amount of bugs and issues that end up in production.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.coderabbit.ai/whitepapers/state-of-AI-vs-human-code-generation-report?" rel="noopener noreferrer"&gt;READ THE FULL REPORT&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The bottom line
&lt;/h2&gt;

&lt;p&gt;AI coding tools are powerful accelerators, but acceleration without guardrails increases risk. Our analysis shows that AI-generated code is consistently more variable, more error-prone, and more likely to introduce high-severity issues without the right protections in place.&lt;/p&gt;

&lt;p&gt;The future of AI-assisted development isn’t about replacing developers. It’s about building systems, workflows, and safety layers that amplify what AI does well while compensating for what it tends to miss.&lt;/p&gt;

&lt;p&gt;For the teams that want the speed of AI without the surprises, the data is clear: Quality isn’t automatic. It requires deliberate engineering. Even when using AI tools.&lt;/p&gt;

&lt;p&gt;An AI code review tool could also help. &lt;a href="https://app.coderabbit.ai/login?????free-trial" rel="noopener noreferrer"&gt;Try CodeRabbit today&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>webdev</category>
      <category>ai</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>It's harder to read code than to write it (especially when AI writes it)</title>
      <dc:creator>Arindam Majumder </dc:creator>
      <pubDate>Tue, 06 Jan 2026 09:43:32 +0000</pubDate>
      <link>https://forem.com/coderabbitai/its-harder-to-read-code-than-to-write-it-especially-when-ai-writes-it-13ag</link>
      <guid>https://forem.com/coderabbitai/its-harder-to-read-code-than-to-write-it-especially-when-ai-writes-it-13ag</guid>
      <description>&lt;p&gt;"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it."&lt;/p&gt;

&lt;p&gt;Brian Kernighan (co-creator of Unix and co-author of The C Programming Language)&lt;br&gt;
I've been programming since I was ten. When it became a career, I got obsessed with code quality: clean code, design patterns, all that good stuff. My pull requests were polished like nobody's business: well-thought-out logic, proper error handling, comments, tests, documentation. Everything that makes reviewers nod approvingly.&lt;/p&gt;

&lt;p&gt;Then, LLMs came along and changed everything. I don't write that much code anymore since AI does it faster. Developer’s work now mainly consists of two parts: explaining to a model what you need, then verifying what it wrote, right? I’ve become more of a code architect and quality inspector rolled into one.&lt;/p&gt;

&lt;p&gt;And here came a problem I knew all too well from my years as a tech lead:&lt;/p&gt;

&lt;h2&gt;
  
  
  READING CODE IS ACTUALLY HARDER THAN WRITING IT.
&lt;/h2&gt;

&lt;p&gt;As an open-source maintainer and senior developer, I had to review tons of other people's code, and I learned what Kernighan said the hard way. Reading unfamiliar code is exhausting. You have to reverse-engineer someone else's thought process, figure out why they made certain decisions, and consider edge cases they might have missed.&lt;/p&gt;

&lt;p&gt;With my own code, reviewing and adjusting were a no-brainer. I designed it, I wrote it, and the whole mental model was still fresh in my head. Now the code is coming from an LLM and suddenly reviewing "my own code" has become reviewing someone else's code. Except this "someone else" writes faster than I can think and doesn't take lunch breaks.&lt;/p&gt;

&lt;p&gt;AI is supposed to help, but if I want to ship production-grade software now, I actually have more hard work to do than before. The irony!&lt;/p&gt;

&lt;p&gt;And that’s why, for my first blog post since joining CodeRabbit, I wanted to focus on that fact. This is also, incidentally, why I decided to join CodeRabbit. But we’ll get to that part later.&lt;/p&gt;

&lt;h3&gt;
  
  
  We’re human (unfortunately for code quality)
&lt;/h3&gt;

&lt;p&gt;Here's where things get uncomfortable: we're human beings, not code-reviewing machines. And human brains don't want to do hard work, thoroughly reviewing something that a) already runs fine, b) passes all the tests, and c) someone else will review anyway. It's so much easier to just git commit &amp;amp;&amp;amp; git push and go grab that well-deserved coffee. Job is done!&lt;/p&gt;

&lt;p&gt;I went from “writing manually and shipping quality code,” to “generating code fast but shipping… bad code!” The quality dropped not because I had less time as I actually had MORE time since I wasn't typing everything myself. I just tend to “shorten” this verification phase, telling myself "it works, the tests pass, the team will catch anything major."&lt;/p&gt;

&lt;h3&gt;
  
  
  The problem with "Catching it in review"
&lt;/h3&gt;

&lt;p&gt;At this point, I was already using CodeRabbit to review my team's pull requests (as an OSS-focused dev, I was an early adopter), and those reviews were genuinely helpful! CodeRabbit would catch things that slipped through. Security issues, edge cases, some logic bugs. Those problems that are easy to miss when you're moving fast.&lt;/p&gt;

&lt;p&gt;But here's the thing: those reviews were coming too late. The code was already pushed. Already in the repository, visible to the entire team. Sure, CodeRabbit would flag the issues and I'd fix them but not before my teammates had seen my AI-generated code with obvious problems that I didn't bother to review properly.&lt;/p&gt;

&lt;p&gt;That's not a great look when you've spent decades building a reputation for quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter: CodeRabbit in an IDE
&lt;/h2&gt;

&lt;p&gt;Then, I discovered CodeRabbit had an IDE extension. The AI code reviewer I was already using for PRs could also review my code locally, before anything hits the repo. This was exactly what I needed.&lt;/p&gt;

&lt;p&gt;When I ask CodeRabbit to check or simply stage my changes, CodeRabbit reviews them right in VS Code, catching issues before git push. Now, my team sees only the polished version, just like the old days. Except now, I'm shipping AI-generated code at AI speeds. And I’m doing it with actual quality control. Automatic reviews mean no willpower required: I don't have to remember to run it, I don't have to open a separate tool. It just happens at commit time. Reviewing doesn't feel like plowing in the rain anymore.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1i94vhg42uzs6bi8qo90.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.amazonaws.com%2Fuploads%2Farticles%2F1i94vhg42uzs6bi8qo90.png" alt="Image" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This gets critical when you're looking at potential security headaches, like the one on the screenshot. CodeRabbit caught an access token leak that could've been a total disaster! Issues like this needs to be addressed before that code gets pushed to a repository.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzn903csot18bmqvtfspv.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.amazonaws.com%2Fuploads%2Farticles%2Fzn903csot18bmqvtfspv.png" alt="Image" width="800" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;More than that, when it finds something, the fixes are committable. The tool doesn’t tell me to "go figure it out" but gives actual suggestions I can apply immediately, in one click.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fje2b04jyiwycn1nobso6.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.amazonaws.com%2Fuploads%2Farticles%2Fje2b04jyiwycn1nobso6.png" alt="Image" width="800" height="690"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For more advanced cases that can’t be resolved with a simple fix, CodeRabbit IDE extension writes a prompt that it sends to an AI agent of your choice. Fun fact: CodeRabbit is so good in writing prompts so I got a lot to learn from, improving my Prompt Engineering skills!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcxllbsls798ie3k4f5x6.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.amazonaws.com%2Fuploads%2Farticles%2Fcxllbsls798ie3k4f5x6.png" alt="Image" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even the free CodeRabbit IDE Review plan offers incredibly helpful feedback and catches numerous issues. However, the Pro plan unlocks its true power, providing the same comprehensive coverage you expect from regular CodeRabbit Pull Request reviews: tool runs, Code Graph analysis, and much more - there is a huge infrastructure behind every check!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8mxwryatnfczbgx6duin.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.amazonaws.com%2Fuploads%2Farticles%2F8mxwryatnfczbgx6duin.png" alt="Image" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The bottom line
&lt;/h2&gt;

&lt;p&gt;Brian Kernighan was right: reading code is harder than writing it. That was true in 1974 and it's even more true now when AI can generate 300 lines while you're still thinking about a variable name.&lt;/p&gt;

&lt;p&gt;We thought AI would make our jobs easier. And it does… if you only count the writing. But the reading verifying, reviewing, and understanding what the AI agent actually built? That got harder.&lt;/p&gt;

&lt;p&gt;Many of us are doing 10x the volume at 10x the speed, which means 10x more code to read with the same human brain that gets lazy and wants coffee breaks. The solution isn't to slow down or go back to typing everything manually. The solution is to automate the code review process as thoroughly as we automated the code writing process. If your AI writes the code, another AI should be reading it before you get to it.&lt;/p&gt;

&lt;p&gt;The quality of the reviews is why I recently transitioned from being a CodeRabbit user to joining the team. And that’s why you should also try CodeRabbit in your IDE. The free tier means there's basically no excuse not to try it. Your reputation will thank you.&lt;/p&gt;

&lt;p&gt;Get started today with a 14-day free trial!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>ai</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Behind the curtain: What it really takes to bring a new model online at CodeRabbit</title>
      <dc:creator>Arindam Majumder </dc:creator>
      <pubDate>Tue, 06 Jan 2026 09:38:11 +0000</pubDate>
      <link>https://forem.com/coderabbitai/behind-the-curtain-what-it-really-takes-to-bring-a-new-model-online-at-coderabbit-5a8f</link>
      <guid>https://forem.com/coderabbitai/behind-the-curtain-what-it-really-takes-to-bring-a-new-model-online-at-coderabbit-5a8f</guid>
      <description>&lt;p&gt;When we published &lt;a href="https://www.coderabbit.ai/blog/the-end-of-one-sized-fits-all-prompts-why-llm-models-are-no-longer-interchangeable?" rel="noopener noreferrer"&gt;our earlier article&lt;/a&gt; on why users shouldn't choose their own models, we argued that model selection isn't a matter of preference, it's a systems problem. This post explains exactly why.&lt;/p&gt;

&lt;p&gt;Bringing a new model online at CodeRabbit isn't a matter of flipping a switch; it's a multi-phase, high-effort operation that demands precision, experimentation, and constant vigilance.&lt;/p&gt;

&lt;p&gt;Every few months, a new large-language model drops with headlines promising “next-level reasoning,” “longer context,” or “faster throughput.” For most developers, the temptation is simple: plug it in, flip the switch, and ride the wave of progress.&lt;/p&gt;

&lt;p&gt;We know that impulse. But for us, adopting a new model isn’t an act of curiosity, it’s a multi-week engineering campaign.&lt;/p&gt;

&lt;p&gt;Our customers don’t see that campaign, and ideally, they never should. The reason CodeRabbit feels seamless is precisely because we do the hard work behind the scenes evaluating, tuning, and validating every model before it touches a single production review. This is what it really looks like.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The curiosity phase: Understanding the model’s DNA
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fymzsgeonijlbxqy8d232.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.amazonaws.com%2Fuploads%2Farticles%2Fymzsgeonijlbxqy8d232.png" alt="Image" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every new model starts with a hypothesis. We begin by digging into what it claims to do differently: is it a reasoning model, a coding model, or something in between? What’s its architectural bias, its supposed improvements, and how might those capabilities map to our existing review system?&lt;/p&gt;

&lt;p&gt;We compare those traits against the many model types that power different layers of our context-engineering and review pipeline. The question we ask isn’t, “is this new model better?” but, “where might it fit?” Sometimes it’s a candidate for high-reasoning diff analysis; other times, for summarization or explanation work. Each of those domains has its own expectations for quality, consistency, and tone.&lt;/p&gt;

&lt;p&gt;From there, we start generating experiments. Not one or two, but dozens of evaluation configurations across parameters like temperature, context packing, and instruction phrasing. Each experiment feeds into our evaluation harness, which measures both quantitative and qualitative dimensions of review quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The evaluation phase: Data over impressions
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frh7uopmqak5kahhsnxdp.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.amazonaws.com%2Fuploads%2Farticles%2Frh7uopmqak5kahhsnxdp.png" alt=" " width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This phase takes time. We run models across our internal evaluation set, collecting hard metrics that span coverage, precision, signal-to-noise, and latency. These are the same metrics that underpin the benchmarks we’ve discussed in earlier posts like &lt;a href="https://www.coderabbit.ai/blog/benchmarking-gpt-5-why-its-a-generational-leap-in-reasoning?" rel="noopener noreferrer"&gt;Benchmarking GPT-5&lt;/a&gt;, &lt;a href="https://www.coderabbit.ai/blog/claude-sonnet-45-better-performance-but-a-paradox?" rel="noopener noreferrer"&gt;Claude Sonnet 4.5: Better Performance, but a Paradox&lt;/a&gt;, &lt;a href="https://www.coderabbit.ai/blog/gpt-51-for-code-related-tasks-higher-signal-at-lower-volume?" rel="noopener noreferrer"&gt;GPT-5.1: Higher signal at lower volume&lt;/a&gt;, and &lt;a href="https://www.coderabbit.ai/blog/opus-45-for-code-related-tasks-performs-like-the-systems-architect?" rel="noopener noreferrer"&gt;Opus 4.5: Performs like the systems architect&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But numbers only tell part of the story. We also review the generated comments themselves by looking at reasoning traces, accuracy, and stylistic consistency against our current best-in-class reviewers. We use multiple LLM-judge recipes to analyze tone, clarity, and helpfulness, giving us an extra lens on subtle shifts that raw metrics can’t capture.&lt;/p&gt;

&lt;p&gt;If you’ve read our earlier blogs, you already know why this is necessary: models aren’t interchangeable. A prompt that performs beautifully on GPT-5 may completely derail on Sonnet 4.5. Each has its own “prompt physics.” Our job is to learn it quickly and then shape it to behave predictably inside our system.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The adaptation phase: Taming the differences
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc8i3bishj754ijgdv5kk.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.amazonaws.com%2Fuploads%2Farticles%2Fc8i3bishj754ijgdv5kk.png" alt="Image" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once we understand where a model shines and where it struggles, we begin tuning. Sometimes that means straightforward prompt adjustments such as fixing formatting drift or recalibrating verbosity. Other times, the work is more nuanced: identifying how the model’s internal voice has changed and nudging it back toward the concise, pragmatic tone our users expect.&lt;/p&gt;

&lt;p&gt;We don’t do this by guesswork. We’ll often use LLMs themselves to critique their own outputs. For example: “This comment came out too apologetic. Given the original prompt and reasoning trace, what would you change to achieve a more direct result?” This meta-loop helps us generate candidate prompt tweaks far faster than trial and error alone.&lt;/p&gt;

&lt;p&gt;During this period, we’re also in constant contact with model providers, sharing detailed feedback about edge-case behavior, bugs, or inconsistencies we uncover. Sometimes those conversations lead to model-level adjustments; other times they inform how we adapt our prompts around a model’s quirks.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The rollout phase: From lab to live traffic
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fplu14qrd5e41h3v0849f.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.amazonaws.com%2Fuploads%2Farticles%2Fplu14qrd5e41h3v0849f.png" alt=" " width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When a model starts to perform reliably in offline tests, we move into phased rollout.&lt;/p&gt;

&lt;p&gt;First, we test internally. Our own teams see the comments in live environments and provide qualitative feedback. Then, we open an early-access phase with a small cohort of external users. Finally, we expand gradually using a randomized gating mechanism so that traffic is distributed evenly across organization types, repo sizes, and PR complexity.&lt;/p&gt;

&lt;p&gt;Throughout this process, we monitor everything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comment quality and acceptance rates&lt;/li&gt;
&lt;li&gt;Latency, error rates, and timeouts&lt;/li&gt;
&lt;li&gt;Changes in developer sentiment or negative reactions to CodeRabbit comments&lt;/li&gt;
&lt;li&gt;Precision shifts in suggestion acceptance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we see degradation in any of these signals, we roll back immediately or limit exposure while we triage. Sometimes it’s a small prompt-level regression; other times, it’s a subtle style drift that affects readability. Either way, we treat rollout as a living experiment, not a switch-flip.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. The steady-state phase: Continuous vigilance
&lt;/h2&gt;

&lt;p&gt;Once a model is stable, the work doesn’t stop. We monitor it constantly through automated alerts and daily evaluation runs that detect regressions long before users do. We also listen, both to our own experience (we use CodeRabbit internally) and to customer feedback.&lt;/p&gt;

&lt;p&gt;That feedback loop keeps us grounded. If users report confusion, verbosity, or tonal mismatch, we investigate immediately. Every day, we manually review random comment samples from public repots that use us to ensure that quality hasn’t quietly slipped as the model evolves or traffic scales.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Why we do all this &amp;amp; why you shouldn’t have to
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwox7veok4eoxz6d9mjr4.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.amazonaws.com%2Fuploads%2Farticles%2Fwox7veok4eoxz6d9mjr4.png" alt=" " width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each new model we test forces us to rediscover what “good” means under new constraints. Every one comes with its own learning curve, its own failure modes, its own surprises. That’s the reality behind the promise of progress.&lt;/p&gt;

&lt;p&gt;Could an engineering team replicate this process themselves? Technically, yes. But it would mean building a full evaluation harness, collecting diverse PR datasets, writing and maintaining LLM-judge systems, defining a style rubric, tuning prompts, managing rollouts, and maintaining continuous regression checks. All of this before your first production review!&lt;/p&gt;

&lt;p&gt;That’s weeks of work just to reach baseline reliability. And you’d need to do it again every time a new model launches.&lt;/p&gt;

&lt;p&gt;We do this work so you don’t have to. Our goal isn’t to let you pick a model; it’s to make sure you never have to think about it. When you use CodeRabbit, you’re already getting the best available model for each task, tuned, tested, and proven under production conditions.&lt;/p&gt;

&lt;p&gt;Because “choosing your own model” sounds empowering until you realize it means inheriting all this complexity yourself.&lt;/p&gt;

&lt;p&gt;Takeaway&lt;br&gt;
Model adoption at CodeRabbit isn’t glamorous. It’s slow, meticulous, and deeply technical. But it’s also what makes our reviews consistent, trustworthy, and quietly invisible. Every diff you open, every comment you read, is backed by this machinery. Weeks of evaluation, thousands of metrics, and countless prompt refinements all in service of one thing:&lt;/p&gt;

&lt;p&gt;Delivering the best possible review, every time, without you needing to think about which model is behind it.&lt;/p&gt;

&lt;p&gt;Try out CodeRabbit today. &lt;a href="https://app.coderabbit.ai/login?????free-trial" rel="noopener noreferrer"&gt;Get a free 14-day trial&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>ai</category>
      <category>javascript</category>
    </item>
    <item>
      <title>CodeRabbit's AI Code Reviews now support NVIDIA Nemotron</title>
      <dc:creator>Arindam Majumder </dc:creator>
      <pubDate>Tue, 06 Jan 2026 05:14:16 +0000</pubDate>
      <link>https://forem.com/coderabbitai/coderabbits-ai-code-reviews-now-support-nvidia-nemotron-4a65</link>
      <guid>https://forem.com/coderabbitai/coderabbits-ai-code-reviews-now-support-nvidia-nemotron-4a65</guid>
      <description>&lt;p&gt;TL;DR: Blend of frontier &amp;amp; open models is more cost efficient and reviews faster. NVIDIA Nemotron is supported for CodeRabbit self-hosted customers.&lt;/p&gt;

&lt;p&gt;We are delighted to share that CodeRabbit now supports the NVIDIA Nemotron family of open models among its blend of Large Language Models (LLMs) used for AI code reviews. Support for Nemotron 3 Nano has initially been enabled for CodeRabbit’s self-hosted customers running its container image on their infrastructure. Nemotron is used to power the context gathering and summarization stage of the code review workflow before the frontier models from OpenAI and Anthropic are used for deep reasoning and generating review comments for bug fixes.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Nemotron helps: Context gathering at scale
&lt;/h2&gt;

&lt;p&gt;This new blend of open and frontier models allows us to improve the overall speed of context gathering and improves cost efficiency by routing different parts of the review workflow to the appropriate model family, while delivering review accuracy that is at par with running frontier models alone.&lt;/p&gt;

&lt;p&gt;High quality AI code reviews that can find deep lying and hidden bugs require lots of context gathering related to the code being analyzed. The most frequent (and most token-hungry) work is summarizing and refreshing that context: what changed in the code and does it match developer intent, how do those changes connect with rest of the codebase, what are the repo conventions or custom rules, what external data sources are available to aid the review, etc.&lt;/p&gt;

&lt;p&gt;This context building stage is the workhorse of the overall AI code review process and it is run several times iteratively throughout the review workflow. NVIDIA Nemotron 3 Nano was built for high-efficiency tasks and its large context window (1 million tokens) along with fast speed helps to gather a lot of data and run several iterations of context summarization and retrieval.&lt;/p&gt;

&lt;p&gt;CodeRabbit architecture with Nemotron support&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1767647146546%2F906c3149-a404-471e-ae4e-1c146ccedb7f.png%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" 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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1767647146546%2F906c3149-a404-471e-ae4e-1c146ccedb7f.png%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" alt="img" width="720" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Blend of frontier and Open Models
&lt;/h2&gt;

&lt;p&gt;When you open a Pull Request (PR), CodeRabbit’s code review workflow is triggered starting with an isolated and secure sandbox environment where CodeRabbit analyzes code from a clone of the repo. In parallel, CodeRabbit pulls in context signals from several sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code and PR index&lt;/li&gt;
&lt;li&gt;Linter / Static App Security Tests (SAST)&lt;/li&gt;
&lt;li&gt;Code graph&lt;/li&gt;
&lt;li&gt;Coding agent rules files&lt;/li&gt;
&lt;li&gt;Custom review rules and Learnings&lt;/li&gt;
&lt;li&gt;Issue tickets (Jira, Linear, Github issues)&lt;/li&gt;
&lt;li&gt;Public MCP servers&lt;/li&gt;
&lt;li&gt;Web search&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To dive deeper into our context engineering approach you can check out our blog: &lt;a href="https://www.coderabbit.ai/blog/the-art-and-science-of-context-engineering" rel="noopener noreferrer"&gt;The art and science of context engineering for AI code reviews&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A lot of this context, along with the code diff being analyzed, is used to generate a PR Summary before any review comments are generated. This is where open models come in. Instead of sending all of the context to frontier models, CodeRabbit now uses Nemotron Nano v3 to gather and summarize the relevant context. Summarization is at the heart of every code review and is the key to delivering high signal-to-noise in the review comments.&lt;/p&gt;

&lt;p&gt;After the summarization stage is completed the frontier models (e.g., OpenAI GPT-5.2-Codex and Anthropic Claude-Opus/Sonnet 4.5) perform deep reasoning to generate review comments for bug fixes, and execute agentic steps like review verification, pre-merge checks, and “finishing touches” (including docstrings and unit test suggestions).&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for our customers
&lt;/h2&gt;

&lt;p&gt;CodeRabbit is now enabling Nemotron-3-Nano-30B support (initially for its self-hosted customers) for the context summarization part of the review workflow along with the frontier models from OpenAI and Anthropic. This results in faster code reviews without compromising quality.&lt;/p&gt;

&lt;p&gt;We are also delighted to support the &lt;a href="https://blogs.nvidia.com/blog/open-models-data-tools-accelerate-ai" rel="noopener noreferrer"&gt;announcement from NVIDIA&lt;/a&gt; today about the expansion of its Nemotron family of open models and are excited to work with the company to help accelerate AI coding adoption across every industry.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.coderabbit.ai/contact-us/sales" rel="noopener noreferrer"&gt;Get in touch&lt;/a&gt; with our team to access CodeRabbit’s container image if you would like to run AI code reviews on your self-hosted infrastructure.&lt;/p&gt;

</description>
      <category>nvidia</category>
      <category>webdev</category>
      <category>ai</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
