AI code review benchmarks

6 AI code review tools, run on 11 real pull requests in 6 production open-source codebases and scored against 56 verified defects.

6
Tools
56
Defects
11
Pull requests
6
Repositories

Results

Codity ranked first, with a 89% severity-weighted catch rate, 46% higher than CodeRabbit (61%).

  1. 01Codity89% severity-weighted catch rateLeader45/56 defects caught
  2. 02CodeRabbit61% severity-weighted catch rate−28 pts behind first30/56 defects caught
  3. 03Greptile60% severity-weighted catch rate−29 pts behind first16/27 defects caught
  4. 04CodeAnt59% severity-weighted catch rate−31 pts behind first31/56 defects caught
  5. 05GitHub Copilot55% severity-weighted catch rate−34 pts behind first29/56 defects caught
  6. 06Cursor Bugbot50% severity-weighted catch rate−39 pts behind first10/27 defects caught

Weighted by severity: critical 4, high 3, medium 2, low 1. Each tool is scored only on the pull requests it reviewed.

By severity

Catch rate at each severity level

ToolCritical4 defectsHigh15 defectsMedium16 defectsLow21 defectsAll defectsWeighted
Codity100%4/4100%15/1594%15/1652%11/2180%45/5689%102/114
CodeRabbit100%4/473%11/1538%6/1643%9/2154%30/5661%70/114
Greptile100%1/140%2/575%6/854%7/1359%16/2760%29/48
CodeAnt75%3/453%8/1569%11/1643%9/2155%31/5659%67/114
GitHub Copilot75%3/447%7/1569%11/1638%8/2152%29/5655%63/114
Cursor Bugbot100%1/160%3/563%5/88%1/1337%10/2750%24/48

Methodology

How the benchmark was run

Each pull request is a real change, taken from a public open-source repository and opened on a fork for review. Not every tool reviewed every pull request: each is scored only on the pull requests it ran on, and a dash marks one it did not. Where the same diff was opened twice so more tools could review it, Codity is scored on the run it shared with CodeRabbit, CodeAnt and Copilot.

There is no planted answer key. The defects are every issue any tool raised that holds up when checked against the code, merged where two tools describe the same problem, plus any the verifier found that no tool did. Every tool ran with its default settings.

  • Caught means an explicit, line-level review comment that points at the faulty code and explains its impact.
  • A summary that mentions the area, or a comment on the wrong line, counts as missed.
  • Style nits, documentation and speculative suggestions are not defects. Severity is set from the defect’s real-world impact.
  • Ranking is by severity-weighted catch rate: a critical catch counts 4, high 3, medium 2 and low 1, over the weight of every defect in the set. Raw counts are shown alongside.
Dataset
RepositoryLanguageDefects
LangChainOctoAI integration, output guardrails and human-in-the-loop interruptsPython12
fastlaneApp Store review cut-off, TestFlight upload and Google Play API v3 migrationRuby26
LocalAIReworks how reasoning blocks are separated from model outputGo3
BlueprintMoves documentation pages to an MDX rendererTypeScript4
AppsmithFixes the Git settings modal flowTypeScript2
Cal.comAdmin spam-report table and seated-event attendee privacy in emailsTypeScript9

Case library

Every defect, every tool

Caught caught · Missed missed

LangChainPython · 12 defects

DefectSeverityCodityCodeRabbitGreptileCodeAntGitHub CopilotCursor Bugbot
Top-level octoai import breaks `import langchain` when optional SDK is absentlangchain/llms/octoai_endpoint.pyCriticalCaughtCaughtCaughtCaughtCaughtCaught
embed_query returns List[List[float]] instead of a single vectorlangchain/embeddings/octoai_embeddings.pyHighCaughtCaughtCaughtMissedCaughtCaught
embed_query uses embed_instruction instead of query_instructionlangchain/embeddings/octoai_embeddings.pyMediumCaughtCaughtCaughtCaughtCaughtCaught
OctoAIEndpoint added to langchain.__all__ but never imported in the packagelangchain/__init__.pyMediumCaughtCaughtCaughtCaughtCaughtCaught
OctoAIEndpoint._call silently ignores per-call **kwargs generation optionslangchain/llms/octoai_endpoint.pyLowMissedCaughtCaughtCaughtCaughtMissed
embed_documents annotated List[float] but returns List[List[float]] (breaks ABC typing)langchain/embeddings/octoai_embeddings.pyLowCaughtMissedMissedMissedMissedMissed
OutputGuardrail declares pydantic field of ABC type Fixer; pydantic v1 fails at importlangchain/output_parsers/base.pyCriticalCaughtCaught–CaughtMissed–
ValidationError(text=e) omits required error_message field, so every parse failure raiseslangchain/chains/llm.pyHighCaughtCaught–CaughtMissed–
OutputGuardrail.fix calls self.fixer(...) but Fixer only defines .fix(), raising TypeErrorlangchain/output_parsers/base.pyHighCaughtCaught–CaughtMissed–
LLMChain.generate/agenerate now return a tuple and create_outputs needs prompts, breaking callerslangchain/chains/llm.pyMediumCaughtMissed–MissedMissed–
New LLMChain.output_parser field is never used, so configuring it silently does nothinglangchain/chains/llm.pyMediumCaughtMissed–CaughtMissed–
Indented module docstring at line 1 causes IndentationError; HITL middleware cannot importlibs/langchain_v1/langchain/agents/middleware/human_in_the_loop.pyCriticalCaughtCaught–MissedCaught–
Caught11/129/125/68/126/124/6

fastlaneRuby · 26 defects

DefectSeverityCodityCodeRabbitGreptileCodeAntGitHub CopilotCursor Bugbot
get_reviews crashes with NoMethodError when API returns no reviews, even without upto_datespaceship/lib/spaceship/tunes/tunes_client.rbHighCaughtCaughtCaughtCaughtCaughtCaught
Reviews exactly at upto_date are kept or dropped depending on page boundariesspaceship/lib/spaceship/tunes/tunes_client.rbLowCaughtCaughtCaughtMissedMissedMissed
Integer ms/1000 truncation drops reviews in the first second after the cutoffspaceship/lib/spaceship/tunes/tunes_client.rbLowMissedMissedCaughtCaughtMissedMissed
Skip-login TestFlight upload crashes: nil Spaceship::Tunes.client in provider inferencepilot/lib/pilot/build_manager.rbHighCaughtCaught–CaughtMissed–
Export-compliance path calls wait_for_build_processing_to_be_complete without argspilot/lib/pilot/build_manager.rbHighCaughtCaught–MissedMissed–
Shell provider-list command interpolates username without shell escapingfastlane_core/lib/fastlane_core/itunes_transporter.rbLowCaughtCaught–CaughtMissed–
installed_packages keeps trailing \r from CRLF adb output so uninstall is skippedscreengrab/lib/screengrab/runner.rbLowCaughtMissed–MissedMissed–
Frameit missing-offset error prints empty path once offsets are cachedframeit/lib/frameit/offsets.rbLowMissedCaught–CaughtMissed–
All skip_upload_* options default to true, so supply uploads nothing by defaultsupply/lib/supply/options.rbCriticalCaughtCaught–CaughtCaught–
promote_track crashes with NoMethodError when version_code is not givensupply/lib/supply/uploader.rbHighCaughtCaught–CaughtCaught–
promote_track promotes track_from.releases.first instead of the filtered releasesupply/lib/supply/uploader.rbHighCaughtCaught–CaughtCaught–
fetch_track_and_release uses Array#first with a block, always returning first releasesupply/lib/supply/uploader.rbHighCaughtMissed–CaughtMissed–
check_superseded_tracks calls new 2-arg update_track with 3 args (ArgumentError)supply/lib/supply/uploader.rbHighCaughtCaught–MissedCaught–
validate_only always fails: second begin_edit hits the still-active validated editsupply/lib/supply/uploader.rbHighCaughtCaught–CaughtCaught–
Metadata-only uploads fail because a track release lookup is always requiredsupply/lib/supply/uploader.rbHighCaughtCaught–MissedCaught–
New release_status option is never applied to created track releasessupply/lib/supply/uploader.rbMediumCaughtMissed–CaughtCaught–
latest_version picks release by lexical name (e.g. 9.0 over 10.0)supply/lib/supply/client.rbMediumCaughtCaught–CaughtCaught–
supply init crashes when selected track has no releases (nil.name / nil releases)supply/lib/supply/setup.rbMediumCaughtMissed–CaughtCaught–
track_version_codes calls flat_map on nil releases before the || [] fallbacksupply/lib/supply/client.rbMediumCaughtCaught–CaughtCaught–
update_rollout/fetch_track_and_release dereference nil track.releasessupply/lib/supply/uploader.rbLowMissedMissed–MissedCaught–
Changelog upload aborts on any locale lacking <version_name>.txt (was skipped)supply/lib/supply/uploader.rbMediumCaughtMissed–MissedMissed–
Listings/images/screenshots re-uploaded once per version codesupply/lib/supply/uploader.rbLowCaughtMissed–MissedCaught–
Client#tracks returns nil when Play API omits empty tracks arraysupply/lib/supply/client.rbLowCaughtMissed–MissedMissed–
release_listings fallback to 'beta' empties track list and then dereferences nilsupply/lib/supply/client.rbLowMissedMissed–MissedCaught–
update_track marks rollout=1.0 as inProgress with userFraction 1supply/lib/supply/uploader.rbLowMissedCaught–MissedMissed–
Supply specs still use V2 Androidpublisher constant and 3-arg update_tracksupply/spec/client_spec.rbMediumMissedMissed–MissedCaught–
Caught20/2615/263/314/2615/261/3

LocalAIGo · 3 defects

DefectSeverityCodityCodeRabbitGreptileCodeAntGitHub CopilotCursor Bugbot
Forced-open mode re-runs closing-only logic on answer text, so the PR's own test failspkg/reasoning/reasoning.goHighCaughtMissedMissedMissedMissedCaught
Stray closing tag after a paired block turns earlier visible answer into reasoningpkg/reasoning/reasoning.goMediumCaughtMissedCaughtCaughtCaughtCaught
Forced-open auto-detect never fires when use_tokenizer_template is set, since predInput is emptycore/http/endpoints/openai/chat.goMediumCaughtCaughtCaughtMissedCaughtMissed
Caught3/31/32/31/32/32/3

BlueprintTypeScript · 4 defects

DefectSeverityCodityCodeRabbitGreptileCodeAntGitHub CopilotCursor Bugbot
Lazy MDX page loads after Documentation's scroll/highlight hooks run, breaking deep linkspackages/docs-app/src/components/mdxRegistry.tsMediumCaughtMissedMissedCaughtMissedMissed
MDX code fence `ts copy` no longer wrapped in .docs-copyable-import, so copy button disappearspackages/docs-theme/src/components/mdxComponents.tsxLowMissedMissedCaughtMissedCaughtMissed
?examples mode no longer hides Alert prose because MDX wrapper is not inside .docs-sectionpackages/docs-theme/src/components/page.tsxLowMissedMissedCaughtCaughtMissedMissed
getMdxComponents called per render gives new component types, remounting examples on theme togglepackages/docs-app/src/components/blueprintDocs.tsxLowCaughtCaughtMissedCaughtCaughtMissed
Caught2/41/42/43/42/40/4

AppsmithTypeScript · 2 defects

DefectSeverityCodityCodeRabbitGreptileCodeAntGitHub CopilotCursor Bugbot
ConnectionSuccess tests share an uncleared dispatch mock; call indices only pass by test orderapp/client/src/pages/Editor/gitSync/Tabs/__tests__/ConnectionSuccess.test.tsxLowCaughtMissedCaughtMissedCaughtMissed
handleClickOnBack useCallback omits isGitConnectV2Enabled, so a late flag change uses a stale valueapp/client/src/pages/Editor/gitSync/DisconnectGitModal.tsxLowCaughtCaughtMissedCaughtMissedMissed
Caught2/21/21/21/21/20/2

Cal.comTypeScript · 9 defects

DefectSeverityCodityCodeRabbitGreptileCodeAntGitHub CopilotCursor Bugbot
Null/missing seatsShowAttendees now defaults to showing all seated attendees in emailspackages/emails/templates/attendee-scheduled-email.tsHighCaughtMissedMissedMissedMissedMissed
Reschedule-request attendee filter never runs; builder never sets seats fieldspackages/emails/templates/attendee-was-requested-to-reschedule-email.tsHighCaughtMissedMissedMissedMissedMissed
Reason filter in admin booking reports table is never sent to the APIapps/web/modules/settings/admin/booking-reports-view.tsxMediumCaughtCaughtCaughtCaughtCaughtCaught
Global block stores raw-case booker email/domain so normalized block checks miss itpackages/trpc/server/routers/viewer/admin/addToWatchlist.handler.tsMediumCaughtMissedMissedMissedCaughtCaught
Bulk blocklist modal previews only first report's domain but blocks every selected domainapps/web/modules/settings/admin/components/add-to-blocklist-modal.tsxMediumCaughtMissedCaughtCaughtMissedMissed
Bulk add-to-watchlist is non-transactional and leaves partial state on failurepackages/trpc/server/routers/viewer/admin/addToWatchlist.handler.tsLowMissedCaughtCaughtMissedMissedMissed
USERNAME watchlist type accepted but handler stores email domain as the valuepackages/trpc/server/routers/viewer/admin/addToWatchlist.schema.tsLowCaughtMissedMissedCaughtCaughtMissed
Duplicate reportIds in input cause spurious NOT_FOUND errorpackages/trpc/server/routers/viewer/admin/addToWatchlist.handler.tsLowMissedCaughtMissedCaughtMissedMissed
Delete dialog and details sheet use translation keys absent from en localeapps/web/modules/settings/admin/booking-reports-view.tsxLowCaughtMissedMissedMissedMissedCaught
Caught7/93/93/94/93/93/9

Run it on your own pull requests

The fastest benchmark is your own codebase. Codity reviews your next PR in minutes.