A Harvard study of 718 firms found that coding agents produced 30% more lines of code and no detectable increase in finished work. Most coverage stops there and reads the zero as a verdict on the tools. The paper’s own review data points somewhere else. My reading of that data, and it’s my inference rather than the paper’s stated finding, is that the extra output was real and the review stage absorbed it. That’s an org-design failure, and you can look for it in your own pull request history this week.
The extra code piled up at the review stage
Fiona Chen and James Stratton built the study on Jellyfish data covering 718 firms and about 300 million work events from January 2021 to March 2026. Jellyfish sells engineering analytics. A finding that work stalls somewhere measurable isn’t against its commercial interest, so weigh the source with that in mind. The analysis itself comes from academic economists, and the headline result is not the one an AI vendor would pick.
After teams adopted coding agents, the activity numbers went up. Lines of code rose 30%. Commits rose 20%. Pull requests rose 23%.
Jira issue and epic resolution showed no change the authors could distinguish from zero. Their estimate rules out an output gain above 12%.
The review numbers explain the gap. In the same paper:
- PR review time rose 49%.
- The share of PRs with changes requested nearly doubled.
- Comments per PR rose 35%.
- The share of workers doing review rose 14%.
Put the two lists side by side. More code went in at the top. Review time, the changes-requested rate and comments per PR all rose, even with 14% more people doing review. Resolved tickets came out the bottom flat. The paper reports these as separate effects and doesn’t claim one caused the other. My inference from the pattern: the capacity didn’t vanish. It turned into review load.
Review load grew faster than PR count
The paper estimates each effect separately, so stacking them is my illustration, not a result from the paper. But stacking them shows why a 23% bump in PRs can stall a team.
Take an illustrative 20-person team with 6 regular reviewers, 200 merged PRs a month, a 10-hour median review time, 4 comments per PR, and 15% of PRs sent back with changes requested. Apply the paper’s effects:
before after change
PRs / month 200 246 +23%
Review time (hours) 10.0 14.9 +49%
PR-hours awaiting review 2,000 3,665 +83% (1.23 x 1.49)
Comments / PR 4.0 5.4 +35%
Total comments 800 1,328 +66% (1.23 x 1.35)
Reviewers 6.0 6.84 +14%
Comments per reviewer 133 194 +46% (1.66 / 1.14)
Changes-requested rate 15% 30% ~2x
PRs needing a second pass 30 74
PR count rose 23%. PR-hours spent awaiting review rose 83%. That’s elapsed time in the queue, not reviewer effort, but it’s time finished work sits unshipped. Even with 14% more people reviewing, each reviewer handles roughly 46% more comment traffic. Every changes-requested PR goes back through the queue, and the illustrative team goes from 30 second passes a month to 74.
That’s the mechanism as I read it. Agents raised the rate at which work arrives at review. Nobody raised the rate at which review clears it.
Headcount didn’t move, and that’s the CFO’s problem
The paper’s headcount results are where the budget conversation should start. Total headcount showed a precise zero effect, ruling out a decline above 2.9%. Engineering headcount showed a noisier zero, ruling out a decline above 13.8%.
A CFO who funds agent licenses expecting to shrink the engineering line didn’t get that in this data. My argument, which the paper doesn’t test directly: what the money bought was a review backlog. Buying more seats pushes more work into a queue that’s already behind.
Measure where your capacity went from PR data
You don’t need Jellyfish to check whether your team looks like the paper. The GitHub API has what you need. This script pulls merged PRs, their reviews and their timelines, and prints three numbers per month: time to first review, changes-requested rate, and reviewer share. Time to first review starts at the last ready-for-review event (or PR creation, if the PR never sat in draft) and stops at the first submitted review.
import json, subprocess, sys
from collections import defaultdict
from datetime import datetime
from statistics import median
REPO = sys.argv[1] # e.g. your-org/your-repo
def gh(path):
raw = subprocess.run(["gh", "api", "--paginate", "--slurp", path],
capture_output=True, text=True, check=True).stdout
return [item for page in json.loads(raw) for item in page]
def ts(s):
return datetime.fromisoformat(s.replace("Z", "+00:00"))
def review_start(pr):
events = gh(f"repos/{REPO}/issues/{pr['number']}/timeline?per_page=100")
ready = [ts(e["created_at"]) for e in events
if e.get("event") == "ready_for_review"]
return max(ready) if ready else ts(pr["created_at"])
months = defaultdict(lambda: {"prs": 0, "cr": 0, "hours": [],
"authors": set(), "reviewers": set()})
for pr in gh(f"repos/{REPO}/pulls?state=closed&per_page=100"):
if not pr["merged_at"]:
continue
m = months[pr["merged_at"][:7]]
m["prs"] += 1
if pr["user"]:
m["authors"].add(pr["user"]["login"])
reviews = gh(f"repos/{REPO}/pulls/{pr['number']}/reviews?per_page=100")
start = review_start(pr)
submitted = sorted(ts(r["submitted_at"]) for r in reviews
if r.get("submitted_at") and ts(r["submitted_at"]) >= start)
if submitted:
m["hours"].append((submitted[0] - start).total_seconds() / 3600)
if any(r["state"] == "CHANGES_REQUESTED" for r in reviews):
m["cr"] += 1
m["reviewers"].update(r["user"]["login"] for r in reviews if r["user"])
for month in sorted(months):
m = months[month]
people = m["authors"] | m["reviewers"]
wait = f"{median(m['hours']):6.1f}" if m["hours"] else " n/a"
print(f"{month} prs={m['prs']:4d} "
f"first_review_h={wait} "
f"changes_requested={m['cr'] / m['prs']:.0%} "
f"reviewer_share={len(m['reviewers']) / len(people):.0%}")
A few caveats. It makes two API calls per PR, so run it on one repo at a time or you’ll hit rate limits. PRs merged with no review drop out of the timing median. Reviewer share here means reviewers divided by everyone active in the repo that month, not total headcount. And time to first review is my proxy. It isn’t the paper’s PR review time metric, which comes from Jellyfish’s own definitions, so don’t compare your hours to anything in the paper. Compare your percentage change across the rollout to the paper’s 49%.
Draw a line at your agent rollout month and read the pattern. If PR count and changes-requested rate both climbed while reviewer share stayed flat, you have the paper’s queue. If reviewer share rose and time to first review still climbed, you added reviewers without shrinking the work each one sees. Pair this with the 30-day rework measure from the earlier post on AI coding dashboards, because every fix below trades review time against escaped defects.
Fix review capacity and merge policy before buying more seats
Start by making review planned work. A reviewer rotation on the sprint board, with hours attached, stops review from being the thing people squeeze in after their “real” tickets. Those hours come out of feature work, and the roadmap will show it. That’s the honest price of clearing the queue.
Merge policy is the second lever. Tier the approval rules: changes in low-risk paths that pass tests merge on one approval, while auth, payments and data migrations keep two. You’ll get more escaped defects in the low-risk tier. That’s why the rework number has to sit next to the speed number on the same dashboard.
Then shrink the unit of review. Cap diff size and tell your agents to split work into reviewable pieces. My argument here, not a measured result: a 400-line agent PR gets skimmed in a way that four 100-line PRs don’t. The cost is more review events, each with its own context switch, plus tooling for stacked PRs.
This doesn’t transfer everywhere. On a four-person startup where review is a glance, the constraint is probably upstream: unclear tickets, slow product decisions. Agents there produce code faster against specs nobody agreed on, and no review fix helps. Jira resolution is also an imperfect output measure. Teams that adopt agents may start writing bigger issues, which would hide gains in the ticket count. The review numbers don’t depend on how tickets are sized, though, and they moved.
Newer agents raise the arrival rate, not the review rate
The strongest objection: the data ends in March 2026, and today’s agents are better. Maybe the stall was a property of weaker tools.
Look at where the stall happened. A better agent writes more code, and more of it is plausible. That raises the arrival rate at review. It does nothing for review throughput unless the agent’s output needs fewer changes or the review itself gets automated.
That can work, and the script above will tell you. If changes-requested rate falls after a model upgrade and 30-day rework doesn’t rise, the newer agent is clearing the queue. If PR count jumps again and review time follows, you got the March 2026 result with a faster input.
A hidden 12% gain still leaves most of the extra volume in the queue
The second objection is statistical. “No detectable change” only rules out gains above 12%. A real gain of 5% or 10% could be sitting inside the confidence interval.
Concede it. Then do the arithmetic. PRs rose 23%. At the 12% ceiling, PRs per resolved issue still rose by roughly 10% (1.23 / 1.12 = 1.098). The 49% jump in review time is measured directly, not inferred from a null. A modest hidden gain is the best case for this argument: the tools produced something, less of it reached done than went in, and the review stage holds the difference. A gain that small also caps the headcount math. It can’t pay for the 13.8% engineering cut the paper already ruled out.
So the zero isn’t a verdict on the tools. It’s what happens when output rises and review capacity doesn’t. Go back to the illustrative 20-person team: agents gave it 46 extra PRs a month, and those PRs arrived carrying 1,665 extra PR-hours waiting in review and 44 extra second passes.