- Clean code is code that is easy to understand, easy to update, and easy to maintain.
- It's code you write thinking about your future self, about the next person who will touch it. It's, many times, an exercise in empathy.
- These are not rules to follow blindly. They are practices you can apply so the next person reading your code can understand much more easily what you've done.
Picture this: You're working in a restaurant kitchen with other people. You're making a pizza. Wouldn't it be easier if every container were labeled with the ingredient name, the recipes were written clearly and in order, the tools were organized, and the workstations were clean?
The same goes for code. If you write functions with clear names, separate responsibilities, and follow a code standard, your code will be easier to understand, maintain, and update.
- Reveal intent at first glance. If a name needs a comment to be understood, rewrite it.
- Follow a naming pattern. Variables = nouns (what it IS), functions = verbs (what it DOES), classes = entities (what it REPRESENTS), booleans = questions (
is/can/has). - One word per concept. Don't mix
getData,fetchUser,retrieveProfile. Pick one prefix and stick with it:getData,getUser,getProfile. - Kill the noise.
userList = []→users = [](you can see it's an array).User { userName, userEmail }→User { name, email }(the class already gives context).accUses→userAccount(longer but readable wins).
Picture this: You open a kitchen cabinet and every jar is labeled "P". Is it pepper? Paprika? Parsley? You have to open each one to find out. Now imagine every jar says "Smoked Paprika", "Black Pepper", "Fresh Parsley". You grab the right one on the first try. That's what good naming does: you read the variable and know exactly what it is.
- One function = one thing. If you describe your function saying "it does X and Y", that's already two functions. It should be "my function does X". Period. Split the rest.
- The fewer arguments, the better. Less arguments means less to remember, less ordering issues, less complexity. If you have more than 3, consider passing an object instead.
- No booleans as parameters. If you pass a boolean, you're about to violate rule one, because inside there will be a conditional doing two different things.
Picture this: A Swiss Army knife can do many things, but none of them well. When you need to cut wood, you grab a saw. When you need to open a bottle, you grab an opener. Functions are the same: a dedicated tool for each job beats one tool trying to do everything.
- KISS (Keep It Simple, Stupid). The best solution is often the simplest one. Don't overcomplicate things. It's harder to simplify a difficult problem than to over-engineer it.
- DRY (Don't Repeat Yourself). Don't repeat business logic. If something is constantly repeated, extract it and reuse it. But be careful: not everything that looks similar is actual duplication. Don't force abstractions.
- YAGNI (You Ain't Gonna Need It). Don't implement things nobody asked for or "for the future". 80% will never be implemented, and the 20% that will, will be done differently. Build what's essential right now.
Picture this: You're cooking dinner for friends.
- KISS: Make a simple pasta that works. Don't attempt a 12-course molecular gastronomy menu.
- DRY: If the same sauce goes on three dishes, make one big batch. Don't make three small ones from scratch.
- YAGNI: Don't prep dessert for 20 people when only 6 are coming "in case more show up".
- Forget "this class does one thing". Instead ask yourself: "Who would request a change here?" If it's all from HR, no problem. But if one part changes because of HR, another because of PM, another because of Finance, those are different actors and each one deserves its own class.
- High cohesion, low coupling. Functions that belong together should stay together and reference each other (high cohesion). Between different modules, the less they know about each other, the better (low coupling).
- Don't over-split. Start simple, think about cohesion first, then about actors. If the logic is simple and changes for the same reason, keep it together.
Picture this: In a football team, the goalkeeper saves, the defender blocks, and the striker scores. If the goalkeeper decides to run up and score goals, nobody is covering the net. Each position has one responsibility. When everyone does their job, the team works.
- 95% of the time you don't need comments. Your code should explain itself. If you feel the need to write a comment, ask yourself first: "Can I rename or refactor something so this comment becomes unnecessary?"
- Comment the WHY, not the WHAT. The few times you do need a comment is for things code can't express: business decisions, external service quirks, workarounds with context (e.g. a ticket number).
- Delete toxic comments. Anything that reads like a diary, changelogs, obvious statements, or commented-out code. That's noise. Git remembers for you.
Picture this: Road signs don't have a paragraph explaining what they mean. A stop sign says STOP, not "Please bring your vehicle to a complete halt at this intersection". If a sign needs a manual to understand, it's a bad sign. Same with code: if it needs a comment, rename or refactor first.
- Never return null. You'll constantly have to check if the response is null. Fail or return an exception instead. Also, never swallow errors or leave them undefined. It's dangerous because you won't know where the failure is.
- Give context to your errors. Explain why they failed: what failed, where, and why.
- Business flow and error handling live in different places. The error handling should happen where the error can actually occur, not where you orchestrate the flow.
Picture this: When a patient arrives at the ER, the doctor doesn't say "something hurts". They need specifics: what hurts, where, since when, and why. An error message like "Something went wrong" is like a patient saying "I feel bad". Useless. "Payment failed for user 123: card expired". Now you can fix it.
- How to detect bad code before it's too late? Look for these three categories:
- Bloaters: Long functions, large classes, long parameter lists. If it's big, it probably does too much.
- Couplers: Too many dependencies between modules, or one module knowing too much about another.
- Dispensables: Dead code that's never used, comments that add no value, unnecessary abstractions, duplicated code.
Big → Bloaters. Sticky → Couplers. Garbage → Dispensables.
Picture this: You open the fridge and something smells bad. You don't know exactly what it is yet, but you know something is rotting. You don't ignore it: you find it and throw it out before it ruins everything else. Code smells are the same: you detect them by the "smell" before you see the actual bug.















