Here's the belief most developers now hold: since AI writes roughly 41% of code, clean code principles are a nice-to-have; the model produces something that "works" anyway. The data says the opposite. As of July 2026, the strongest signals show that discipline didn't die, its price went up.
Google's 2025 DORA report found that AI acts as a multiplier of existing engineering practice: it lifts throughput by an estimated 2–18%, but where discipline is weak it also raises the change failure rate. In other words, AI ships bad code faster. That's exactly why this checklist exists, to audit the code that ships, no matter who wrote it, in 30 seconds.
What does clean code actually mean?
Clean code is code that, beyond working correctly, is easy to read, easy to change, and low-risk to touch. In Robert C. Martin's definition, clean code is code where "each function does one thing and does it well." The point isn't to look clever; it's that whoever opens the file six months later (usually you) can move forward without losing context.
This isn't a style preference, it's a maintenance-cost decision. And the cost is real: GitClear's analysis of 211 million lines found code churn rising from 4.5% in 2023 to 5.7% in 2024, refactoring down 39.9%, and copy-pasted lines up 17.1%. The "we shipped it fast" feeling returns as maintenance debt 30 to 90 days later.
The clean code principles checklist (step by step)
Follow this order when reviewing a pull request. Each item is independently checkable, so don't try to fix them all at once:
- Read names for intent. Do variable, function, and class names explain what they do without a comment? Names like
d,tmp, ordata2should be renamed. - Measure function size. Each function should carry one responsibility; consider splitting bodies over 20 lines, and almost always split anything over 50.
- Question every comment. Does the comment explain "why" rather than "what"? Delete comments that restate the code; keep ones that explain a hidden decision.
- Hunt duplication. This is where AI assistants stumble most: is the same logic copied in two or three places? Extract DRY violations into a shared function, but avoid premature abstraction.
- Reduce nesting. If
if/forblocks nest deeper than 3 levels, flatten them with early returns (guard clauses). - Clear out magic numbers. Inline values like
86400or0.15should move to named constants. - Audit error handling. Are errors swallowed silently? Every error should be handled or propagated with meaningful context.
- Check the tests. Does the changed logic have a test, and do the tests verify behavior rather than implementation details?
What's the difference between a good name and a bad one?
A good name lets the reader understand intent without looking at the code; a bad name forces them back to the source. The strongest signal: if you read a function or variable name and don't feel the urge to look inside, the name is good. Naming is the highest-return investment in the whole clean code principles checklist because it pays off on every single read.
The comparison below shows the smells we see most often and their cleaned-up versions:
Smell | Bad example | Clean version | Why |
|---|---|---|---|
Cryptic abbreviation | | | Intent is explicit, no guessing |
Boolean ambiguity | | | Carries the answer in its name |
Type suffix | | | Type is already known, adds noise |
Vague verb | | | What it does is measurable |
Magic number | | | The decision's meaning is visible |
On one team project we found 12 different functions named process(); none said what they "processed." After renaming, review time dropped noticeably because nobody had to dive into the body.
How small should functions be?
A function should do one thing at one level of abstraction; a practical target is usually under 20 lines. Size isn't the goal itself, it's a measurable proxy for the single-responsibility principle. If you have to say "and" when describing a function (it fetches data and validates and saves), it probably wants to be three functions.
The example below shows how a mixed-up function gets decomposed:
// Before: one function doing three jobs
function saveUser(input) {
if (!input.email.includes('@')) throw new Error('bad email');
const record = { ...input, createdAt: Date.now() };
db.insert('users', record);
}
// After: each step is a separate, named function
function saveUser(input) {
validateEmail(input.email);
const record = buildUserRecord(input);
return persistUser(record);
}The second version looks longer, but each piece is independently testable and the saveUser body now reads like a summary. To go deeper, see our software design patterns guide and, for type-level cleanliness, our advanced TypeScript patterns article.
Does the linter finish the job, or do you still need human review?
They're two different layers; neither replaces the other. The linter catches mechanical rules (formatting, unused variables, magic numbers); the human judges intent and design. But the tooling moves fast, so keep your versions current:
Tool | Current status (Jul 2026) | What it catches | Note |
|---|---|---|---|
v10 (Feb 2026); v9 EOL Aug 6, 2026 | Style, likely bugs, usage patterns | | |
2026.1 LTA | Complexity, code smells, security | Stricter "Sonar way for AI Code" quality gate for AI-written code | |
Prettier | v3.x | Formatting only | Ends the debate, doesn't measure quality |
Human review | — | Intent, naming, design | The one layer you can't automate |
Practical rule: push everything mechanical into CI so human review focuses only where judgment is needed. It's no accident SonarQube now ships a separate "AI Code" gate; auditing assistant-generated code more strictly is now mainstream.
Should I write a comment or fix the code?
The rule is simple: the urge to write a comment is often a sign the code isn't clear enough. Try to make it unnecessary by fixing the name and structure first; only write a comment when the "why" can't be inferred from the code.
- Delete: Comments that restate code, like
i++; // increment i. - Write: Comments that surface hidden knowledge, like
// Provider API returns 500 for requests over 30s, so 25s timeout. - Convert: A long explanatory comment can usually become a well-named helper function.
- Keep them current: A wrong comment is worse than no comment; update the comment when you change the code.
Where do tests fit in clean code?
Tests are clean code's safety net: without good tests, every refactoring is a gamble. A clean test suite verifies behavior, not implementation detail, so tests survive when you change the internals. In review, check: is coverage behavior-focused, do test names reveal intent, are tests isolated, and are edge cases (empty input, null, boundary values) covered?
If you want to embed test-first work in the team, see our guide on how to write unit tests that actually help and the wider frame on our software engineering category page.
The most common clean code mistakes
Well-intentioned habits that backfire, seen over and over in hundreds of reviews:
- Premature abstraction. Generalizing logic with fewer than two instances because "we'll need it later" makes code unreadable. See the duplication first, then abstract.
- Accepting AI output blindly. Remember the eight-fold jump in duplicated blocks; the mistakes people make with AI coding assistants usually stem from the "it works, so it must be clean" fallacy.
- Papering over bad code with comments. Instead of a long comment explaining a complex block, extract the block into a named function.
- Cleaning everything in one shot. A massive "cleanup" PR is unreviewable; small, focused changes always win. For old code, follow a safe legacy refactoring approach.
The goal isn't perfection, it's leaving the code a little cleaner than you found it.
Frequently Asked Questions
Do clean code principles still matter now that AI writes code?
More than before. In the 2025 DORA report, 59% of respondents saw a positive AI effect on code quality, yet 30% report little or no trust in the code it generates. AI is a multiplier of existing discipline; on a team without a checklist, the only thing that speeds up is the technical debt.
Do clean code principles hurt performance?
Usually not. Splitting functions or naming variables is already optimized away by modern compilers and JITs, so it rarely has a measurable cost. If you hit a bottleneck on a genuine hot path, measure and optimize locally, but don't sacrifice the whole codebase's readability for it.
How do I apply this checklist in code review?
Add the list to your PR template as a checkbox block and go top to bottom on each review. It slows you down for the first few weeks, but within a few sprints the items become automatic. If a PR has more than five violations, a short pairing session with the author beats commenting on each one.
Where should I start in a legacy codebase?
Don't do a big top-to-bottom cleanup; apply the "boy scout rule" instead: leave every file you touch a little cleaner than you found it. Only improve code you're already working on, protect it with a test, and keep the change small. The codebase then improves over time without taking on risk.



