TL;DR — AI assistants are producing C# that looks correct and passes review, but reintroduces production regressions we spent years training out of teams. I'm trying to find out whether other .NET teams see the same patterns — and what's actually catching them before merge.
More AI-generated C# is landing in pull requests. Most of it is fine.
But a specific category keeps slipping through — and it's the dangerous one, because it compiles, tests pass, and a human skim says "looks good."
The pattern
The code compiles. Tests pass. Review approves. Production finds out.
These aren't syntax errors. They're architectural intent violations — the kind of thing a senior dev would have caught in review before PR volume tripled.
Five regressions I keep seeing
1. EF Core read paths without AsNoTracking()
Fine in dev. Expensive on a hot read path in prod.
// ❌ Looks reasonable. Tracks entities you never mutate.
var orders = await _db.Orders
.Where(o => o.CustomerId == id)
.ToListAsync(cancellationToken);
Fix direction: AsNoTracking() on read-only queries, or a team convention documented in CLAUDE.md / Copilot instructions.
2. Captive dependency (scoped service in a singleton)
Compiles. Runs. Wrong state across requests.
// ❌ Singleton lives forever; scoped dependency does not.
services.AddScoped<IOrderRepository, OrderRepository>();
services.AddSingleton<ReportCache>(); // ctor takes IOrderRepository
Fix direction: align lifetimes, or inject IServiceScopeFactory instead of capturing scoped services.
3. Dropped CancellationToken
The method accepts cancellation. The downstream call ignores it.
// ❌ Signature honours cancellation; body doesn't.
public async Task RunAsync(CancellationToken cancellationToken)
{
await Task.Delay(500); // overload with token exists
}
Fix direction: forward cancellationToken to every downstream async call that accepts one.
4. Swallowed exception
Failure disappears. Monitoring stays green.
// ❌ "Handle errors gracefully" — AI edition.
try
{
await SaveAsync(cancellationToken);
}
catch (Exception)
{
// ignore
}
Fix direction: log and rethrow, or handle a specific exception type with real recovery logic.
5. Task.Run over blocking I/O
Looks async. Still blocks a thread pool thread.
// ❌ Offloads the blocking call; doesn't remove it.
var json = await Task.Run(() => File.ReadAllText(path));
Fix direction: ReadAllTextAsync, OpenReadAsync, or other truly async I/O APIs.
Why review misses them
| What reviewers see | What production sees |
|---|---|
| Clean diff, idiomatic C# | Tracking overhead, stale scoped state, ignored cancellation |
| Green CI | Incidents that are hard to trace back to a specific PR |
| "We have analyzers" | Warnings buried in the IDE or not enforced on merge |
The assistant doesn't carry your team's hard-won context — lifetime rules, EF conventions, "we always forward the token" — and reviewers are skimming more PRs than before.
Why IDE warnings aren't enough (maybe)
Roslynator, Sonar, and the built-in analyzers catch a lot of generic quality issues. That part is in decent shape.
The gap I keep feeling is less about detecting these while someone is typing — where warnings get ignored — and more about enforcing them in CI, on the PR, so they can't merge.
I'm not convinced the missing piece is "another rule in the IDE." I think it's "this can't ship unless it's fixed."
Two questions for you
I'm genuinely trying to compare notes — not selling anything.
- Are you seeing these AI-introduced regressions slip past review? Which ones — if any — show up most on your team?
-
If you've tightened things up — how? Custom analyzers? Stricter CI?
.editorconfigseverity as errors? Team-specific rules checked automatically? Or still mostly discipline and careful review?
Drop a comment with what's working (or what's failing) on your team. Especially interested in hearing from tech leads and EMs running small-to-mid .NET teams with a steady flow of AI-assisted PRs.
No product pitch — mapping whether this is a real, recurring team problem or just my bubble. Thanks for reading.
Top comments (0)