Clean code is code that another developer (or you, six months from now) can read, understand and change safely. It is not about clever tricks or following a style guide word by word. It is about lowering the cost of every future change.
In this guide we go through the golden rules of clean code with short, practical examples in JavaScript, PHP and SQL, and finish with a checklist you can use in your next code review.
Why Is Clean Code Important?
A significant portion of a software project's lifecycle is spent on maintenance and updates. As Robert C. Martin notes in Clean Code, the time we spend reading code far exceeds the time we spend writing it. Clean code is the most valuable legacy we leave for our future selves and our teammates.
Messy code has a real cost:
- Slower features: every change takes longer because nobody is sure what a piece of code does.
- More bugs: hidden side effects and duplicated logic break in places you did not expect.
- Harder onboarding: new team members need weeks instead of days to become productive.
- Fear of change: when code is hard to understand, people stop improving it and the problem grows.
The Golden Rules of Clean Code
1. Use Meaningful Names
Your variable, function and class names should clearly say what they hold or do. Avoid ambiguous names like $d, $list1 or get_stuff(). A good name removes the need for a comment.
// BAD
let d = 20; // 20 what? Days? Age?
function handle(u) { /* ... */ }
// GOOD
const maxLoginAttempts = 20;
const elapsedTimeInDays = 20;
function sendWelcomeEmail(user) { /* ... */ }
Some simple naming rules that work in every language:
- Functions are verbs (
calculateTotal,sendInvoice); classes and variables are nouns (Invoice,activeUsers). - Booleans read like a question:
isActive,hasPermission,canRetry. - Keep one word per concept. Do not mix
fetch,getandretrievefor the same kind of action. - Replace magic numbers with named constants.
2. Functions Should Do One Thing
A function should do one thing, do it well, and do it only. If your function "gets the user, validates the email and sends a welcome mail", it is doing three jobs. This idea is closely related to the Single Responsibility Principle (SRP): a unit of code should have only one reason to change.
// BAD: one function, three responsibilities
function register(array $data)
{
if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Invalid email');
}
$id = $this->db->table('users')->insert($data);
mail($data['email'], 'Welcome!', 'Thanks for joining.');
return $id;
}
// GOOD: small functions with clear names
function register(array $data): int
{
$this->validateRegistration($data);
$userId = $this->users->create($data);
$this->mailer->sendWelcome($data['email']);
return $userId;
}
Small functions are easier to name, easier to test and easier to reuse. A useful rule of thumb: if you cannot describe what a function does without using the word "and", it probably does too much.
3. Keep Functions Short and Flat
Deeply nested if blocks force the reader to keep many conditions in mind at once. Use guard clauses (early returns) to handle edge cases first and keep the main path flat.
// BAD: nested conditions
function getDiscount(user) {
if (user) {
if (user.isActive) {
if (user.orders > 10) {
return 0.15;
}
}
}
return 0;
}
// GOOD: guard clauses
function getDiscount(user) {
if (!user || !user.isActive) return 0;
if (user.orders <= 10) return 0;
return 0.15;
}
4. Limit Function Arguments
A function with five positional arguments is hard to call correctly. Was the third one true for "send email" or for "is admin"? When a function needs many values, group them into an object or a small data class, and avoid boolean flag arguments that switch behavior.
// BAD
createUser('Ada', 'ada@example.com', true, false, 'admin');
// GOOD
createUser({ name: 'Ada', email: 'ada@example.com', role: 'admin', sendWelcome: true });
5. Avoid Excessive Commenting
Well-written code should be self-documenting. If you need a long comment to explain what a block does, you probably need to rewrite that block more clearly, for example by extracting it into a well-named function. Comments are for explaining why the code is written a certain way, not what it does.
"Don't comment bad code — rewrite it." — Brian W. Kernighan and P. J. Plauger
Good comments explain business rules, legal requirements, workarounds for third-party bugs or the reason behind a surprising decision. Commented-out code does not belong in the repository; version control already remembers it.
6. Don't Repeat Yourself (DRY)
If you copy and paste the same block of code in different parts of your project, every future change has to be made in several places, and sooner or later one of them is forgotten. Move shared logic into a reusable function, service or helper.
Be careful not to over-apply DRY, though. Two pieces of code that look similar today but change for different reasons are not really duplicates. Merging them too early creates the wrong abstraction.
7. Handle Errors Explicitly
Clean code does not hide failures. Throw exceptions with clear messages instead of returning magic values like -1 or false, and never swallow errors with an empty catch block.
// BAD: the error disappears silently
try {
$payment->charge($order);
} catch (Exception $e) {
}
// GOOD: log it and let the caller decide
try {
$payment->charge($order);
} catch (PaymentFailedException $e) {
log_message('error', 'Payment failed for order {id}: {msg}', ['id' => $order->id, 'msg' => $e->getMessage()]);
throw $e;
}
8. Keep a Consistent Format
Formatting is not decoration: consistent indentation, spacing and file structure let the reader focus on logic. Agree on a standard (for example PSR-12 for PHP or Prettier for JavaScript) and let tools apply it automatically, so code reviews never discuss spaces and brackets again.
9. Write Tests
Tests are what make refactoring safe. Without them, every cleanup is a risk; with them, you can rename, split and simplify code with confidence. Keep tests readable too: one behavior per test, a clear name such as it_rejects_an_invalid_email, and no complicated logic inside the test.
10. Leave the Code Cleaner Than You Found It
The "Boy Scout Rule" says you should leave the campground cleaner than you found it. You do not need a big rewrite. Rename one unclear variable, extract one long condition or delete one piece of dead code every time you touch a file. Small, continuous improvements keep a codebase healthy.
Clean Code in SQL
Clean code rules are not only for application code. SQL queries are read and changed just as often, and messy queries are a common source of bugs and slow pages.
- Write keywords consistently (for example uppercase
SELECT,FROM,WHERE) and put each clause on its own line. - Select only the columns you need instead of
SELECT *. It documents intent and avoids loading unnecessary data. - Use meaningful aliases like
orders oandcustomers c, nota,b,c. - Use explicit JOINs instead of listing tables in
FROMand joining them inWHERE. - Break complex queries into CTEs (
WITH ... AS) so each step has a name. - Name tables and columns consistently (for example snake_case, plural table names) and stick to it across the schema.
- Always use parameters instead of concatenating user input into queries. It is cleaner and it prevents SQL injection.
-- BAD
select * from orders a, customers b where a.cid=b.id and a.status='paid' and a.created_at>'2026-01-01'
-- GOOD
WITH paid_orders AS (
SELECT o.id, o.customer_id, o.total, o.created_at
FROM orders o
WHERE o.status = 'paid'
AND o.created_at >= :start_date
)
SELECT c.name,
COUNT(p.id) AS order_count,
SUM(p.total) AS revenue
FROM paid_orders p
JOIN customers c ON c.id = p.customer_id
GROUP BY c.name
ORDER BY revenue DESC;
For the performance side of SQL, such as indexes and the N+1 query problem, see our guide on database optimization and Redis caching.
Clean Code Checklist
Use this short checklist before you open a pull request:
- Can someone understand each name without reading its implementation?
- Does every function do one thing, and is it short enough to read without scrolling?
- Are edge cases handled with early returns instead of deep nesting?
- Are there any magic numbers, duplicated blocks or commented-out code left?
- Are errors handled explicitly and logged where needed?
- Is the code formatted automatically according to the team standard?
- Do tests cover the behavior you added or changed?
- Are SQL queries parameterized, readable and selecting only the needed columns?
Conclusion
Writing clean code is not a skill you learn overnight; it is a discipline you practice every day. Start with meaningful names and small functions, add tests so you can refactor safely, and apply the same care to your SQL. Your teammates, and your future self, will thank you. Remember: the first person who will read your code is you.
No comments yet. Be the first to comment!