The Old Tell
Formatting quality and logical correctness used to travel together, not because messy syntax causes bugs, but because they shared a common cause: the author's attention. A developer who couldn't be bothered to align a WHERE clause or close a brace properly usually hadn't been bothered to think through the edge case three lines below it either. Reviewers learned this correlation the hard way, and it became a real, if informal, review heuristic: find the ugly block, read it twice.
It wasn't a perfect signal. Plenty of careful people write ugly code, and plenty of careless people write pretty code. But as a triage tool, applied across hundreds of lines under time pressure, "which part looks rushed" was a genuinely useful place to start looking for the part that was rushed.
What Changed
An AI assistant doesn't get tired, doesn't skip formatting when it's in a hurry, and doesn't leave a variable half-renamed because it ran out of patience halfway through a refactor. Every function gets the same consistent indentation, the same descriptive names, the same tidy error handling scaffolding, whether the code inside that scaffolding is correct or not. The visual signal that used to correlate with author attention now just reflects the formatter, and the formatter doesn't know whether the query is right.
That would be a minor inconvenience if pretty and ugly code were equally likely to be correct. They're not, in one specific and dangerous way: pretty code doesn't just fail to signal a bug, it can actively borrow the visual signature of the correct pattern while still containing the incorrect one. A well-formatted call to sp_executesql looks like the safe, parameterized way to run dynamic SQL, whether or not a parameter was actually used. A tidy try/catch with a logging line inside it looks like proper error handling, whether or not anything is actually done with the exception. The old heuristic didn't just go quiet, it started actively pointing reviewers away from the real problem.
Same Bug, Two Outfits
The three pairs below aren't hypothetical edge cases. Each one is a bug pattern that shows up regularly in real code, once written by a rushed human and now more often generated by an assistant that formats it beautifully. In every pair, the bug is identical. Only the packaging changed.
SQL: dynamic filtering
declare @sql varchar(4000)
set @sql='select * from Orders where CustomerID=''' + @custId + ''' and Status=''' + @status + ''''
exec(@sql)
DECLARE @Sql NVARCHAR(MAX);
SET @Sql = N'
SELECT OrderId,
OrderDate,
Status
FROM dbo.Orders
WHERE CustomerId = ''' + @CustomerId + N'''
AND Status = ''' + @Status + N''';';
EXEC sp_executesql @Sql;
The second version reads as careful T-SQL: proper casing, aligned columns, a fully qualified table name, and sp_executesql instead of a bare EXEC(), the exact procedure most style guides recommend precisely because it supports parameters. But no parameters were passed. The string is still built by concatenation, the injection is still there, and the choice of sp_executesql now works against the reviewer, because it pattern-matches on "the safe call" without anyone checking that it was used safely.
PowerShell: error handling
function Backup-Db{
param($db)
try{
Backup-SqlDatabase -Database $db -BackupFile "D:\Backup\$db.bak"
}catch{}
Write-Host "done"
}
function Backup-SqmDatabase {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Database
)
try {
Backup-SqlDatabase -Database $Database -BackupFile "D:\Backup\$Database.bak"
Write-Verbose "Backup of '$Database' completed successfully."
}
catch {
Write-Verbose "Backup step finished for '$Database'."
}
}
[CmdletBinding()], a mandatory typed parameter, Write-Verbose instead of Write-Host: every individual habit here is exactly what a PowerShell style guide asks for, and a reviewer scanning for those habits finds them all present. What's missing is any actual handling in the catch block. $_ is never read, nothing is logged to the caller, nothing is re-thrown. The failure is swallowed just as completely as the ugly version, but it's swallowed inside a function that looks like it was written by someone who knows what they're doing, which is exactly why it will pass a quick review.
C#: iterating a result set
public void ProcessRows(DataTable dt){
SqlConnection c=new SqlConnection(connStr);
c.Open();
for(int i=0;i<=dt.Rows.Count;i++){
var row=dt.Rows[i];
DoWork(row);
}
}
public void ProcessRows(DataTable table)
{
using var connection = new SqlConnection(_connectionString);
connection.Open();
for (var i = 0; i <= table.Rows.Count; i++)
{
var row = table.Rows[i];
ProcessRow(connection, row);
}
}
The using declaration is a real improvement, the connection really will be disposed correctly, and that's precisely the problem: it's a genuine sign of care that a reviewer notices and, having noticed it, relaxes. The loop bound is still <= instead of <, and table.Rows[table.Rows.Count] will throw IndexOutOfRangeException on every non-empty table. One correct, visible habit bought cover for one incorrect, easy-to-miss one, sitting three lines apart in the same tidy method.
Why This Is Worse Than It Sounds
It would be tempting to read this as "AI code needs the same review as human code," and stop there. It actually needs more scrutiny in one specific way: humans write ugly-and-wrong code often enough that reviewers built a muscle for it, but they never had to build a muscle for pretty-and-wrong, because it used to be rare. That muscle doesn't transfer automatically. A reviewer who has spent years pattern-matching on formatting quality as a proxy for correctness is, ironically, the person most likely to get lulled by a well-formatted sp_executesql call or a disciplined using block. The instinct that used to protect them is now the thing being exploited, not maliciously, just as a side effect of what the model was optimized to produce: plausible, well-formatted code, not necessarily correct code.
What Actually Still Works
None of this means review is hopeless, it means the cheap first-pass heuristic is gone and the remaining ones have to carry more weight than before.
- Read for logic, not for style. Formatting quality is no longer evidence of anything. Every claim a well-formatted block makes about itself, "this is parameterized," "this is handled," "this loop is bounded correctly", has to be checked against what the code actually does, not what it looks like it does.
- Follow every error path to its end, on purpose. Find every
catch, everyWHEN OTHERS, every swallowed non-zero exit code, and ask specifically what happens to the caller when it fires. A log line that doesn't re-throw or bubble up a real signal is functionally the same as an empty block. - Be more suspicious of code that visibly imitates a known-safe pattern, not less.
sp_executesql,using,try/finally, parameterized-looking string builders: check that the safe part is actually doing the safe thing, rather than crediting the pattern on sight. - Push verification into things that don't care about formatting. Tests, static analysis, linters, and query plans judge behavior, not appearance. They are immune to exactly the bias this article is describing, which makes them more valuable now than when messy code did some of that filtering for free.
- Ask "what if this input is null, empty, negative, or one row short" regardless of how the code looks. That question used to get asked selectively, mostly on the ugly parts. It now has to get asked everywhere, because the ugly parts stopped marking themselves.
Sloppy code was never the actual problem, it was a symptom that happened to be visible. AI has made the symptom disappear without touching the underlying rate of mistakes. The formatting got better. The discipline required to find what's actually wrong underneath it just got harder, at exactly the moment more code is shipping with less human attention on any single line of it.