
Writing Clean Code That Lasts
Writing Clean Code That Lasts
Writing Clean Code That Lasts
Renas Hassan
Apr 30th, 2025
I've worked with numerous codebases at different companies, and I've seen both clean and messy code. The costs of writing bad code might not be obvious at first. On the surface, it looks like we're shipping features quickly, but everything has a cost. Eventually, it will bite you, and it's never pretty.
The cost of bad code
Let's exaggerate a bit to see how fast the dominoes fall when nobody cares about code quality.
Let's draw a scenario where you work in a company that lacks guidelines or strict boundaries for the code you're shipping. Code quality is just an afterthought, and everybody is just writing code as they please. Not to mention, upper management is also pushing you with tight deadlines. With every commit to the main branch, you're introducing more and more technical debt. With every new feature, you think to yourself:
Eh, no big deal, I'll refactor this later.
But let's be honest, you'll never refactor that code. You'll be too busy shipping new features. Then the next time someone visits that code, they'll have to spend hours trying to understand it.
They'll find themselves in a situation where they'll be like:
Why is this so complicated?
WTF?
Am I stupid?
They'll face a difficult choice: either invest time in refactoring the existing code or build on top of the shaky foundation. Since they're under pressure to ship, they'll most likely choose the latter. It's harder to write clean code when you're building on top of already bad code, the surrounding chaos will inevitably spill over.
This creates a vicious cycle of bad code, and that's how technical debt compounds. Eventually, the codebase will become so bad that basic features take five times longer to implement. With every new feature, more and more bugs are introduced due to the poor code quality. Now you're having a harder time competing with competitors who are able to ship features way faster.
Eventually, this frustration often leads engineers to reach their breaking point and leave the company. This creates a new challenge as the organization must now hire replacements who lack both familiarity with the codebase and the historical context that the previous team possessed. The new engineers have a harder time getting up to speed, which slows down progress and keeps the technical debt growing.
Eventually, the only logical thing is to do a complete rewrite of the codebase and you're back to square one.
The advantages of clean code
The above is just one example of how bad code can impact a company. Let's try to understand the benefits of writing clean code.
As software engineers, we are going to be reading code A LOT more than we are going to be writing it. It would be best to be able to read and understand the code without having to spend hours trying to understand it due to some poorly written code.
Beyond the time it saves you during development, clean code also offers several practical advantages:
It's easier to:
- maintain
- extend
- test
- debug
- refactor
- collaborate
Don't believe me? Here's how your PR comments start to look like:

Absolute giga chad code, no need for tests, just ship it!

Please teach me senpai

Just push to main at this point, no need for PRs 🤩
Clean code in practice
I'm not going to go through all the principles of clean code in this article, because there are just too many for one article.
If you wish to dive deeper, I recommend reading Clean Code by Robert C. Martin.
However, I do want to give you some quick and impactful clean code practices you can apply today that will give a major boost to your code quality.
Let's start off with a piece of code that is bad and see how we can improve it step by step by applying some of the principles of clean code.
The "bad" code
Here's a function that calculates the final price of an order. It applies a discount based on the user type (1
for regular, 2
for premium) and adds tax.
It works, but it's hard to read and maintain.
There are several issues with this code:
- Excessive comments that explain what the code does.
- Unclear variable names (
items
,userType
,total
). - Magic numbers (
1
,2
,0.95
,0.85
,1.10
). - Multiple responsibilities (calculating subtotal, applying discount, adding tax).
- Weak type safety.
Remove comment noise
I quite commonly see codebases that are filled with comments that explain what the code does. This is merely a band-aid for unclear code. Instead, strive to write code that is self-explanatory. Comments add a lot of noise, require maintenance, and can become outdated.
So when should you use comments?
- When you need to explain why you did something, such as a trade-off, architecture decision, or a business rule.
- When you need to add a
TODO
orFIXME
to remind yourself or others to come back to something.
See the difference for yourself, compare the noisy "bad code" above to the version below, which is much less noisy without explanatory comments:
Add meaningful names and types
The beauty of this principle is that it's so simple that it's easy to overlook. But don't sleep on it, it makes a huge difference. It's about giving names to variables, parameters, and functions that make sense and are descriptive.
So how do you write good names?
- Reveal intent: The name should clearly state what the variable represents or does.
- Be specific & descriptive: Avoid vague terms like
data
ortemp
. Use names likeorderItems
instead of justitems
. - Use relevant terms: Use terms that are specific to the domain or subject matter, such as
discountRate
orcustomerAddress
. - Follow conventions: Prefix boolean variables with
is
orhas
e.g.isLoading
,hasPermission
. - Prioritize clarity: Choose longer, descriptive names instead of short, unclear abbreviations.
- Distinguish meaningfully: Make sure names clearly differentiate variables with similar roles (e.g.,
subtotal
,discountedTotal
,finalTotal
). - Be searchable: Avoid single letters or cryptic names that are hard to find in the codebase like
i
,x
,y
. - Avoid misleading names: Make sure variable names accurately reflect their content or purpose to prevent confusion.
We didn't make any changes to the logic of the code, yet it is now much easier to understand and reads like english.
Here are the improvements we made:
- Variable names: The improved variable names make the code self-documenting.
- Order item type: We added a type for the order item. Only use the
any
type as a last resort. What is the point of using typescript if you're not going to use types? - User type: We changed the
userType
parameter from a number to a string literal. Having to mentally map a number to a user type is error prone and hard to understand. You constantly have to remind yourself that1
isregular
and2
ispremium
. Now when you pass arguments to the function, you get the added benefit of autocompletion.
Eliminate magic numbers
Magic numbers are unnamed numerical constants in code that lack context or explanation.
For example, seeing 0.95
in a calculation without any indication of its purpose can be confusing.
Trying to make sense of magic numbers and what they represent adds extra cognitive load and can send you down a rabbit hole of trying to figure out what a magic number represents.
Also, if you use the magic number in multiple places, you have to update it in every place you use it.
Fortunately, this problem has an easy fix: use named constants. Using named constants instead of hardcoded values for things like discount rates and tax multipliers clarifies the code's purpose and makes future changes easier.
The convention for naming constants in javascript/typescript codebases is to use all caps delimited by underscores.
Using as const
for REGULAR_USER_DISCOUNT_RATE
, PREMIUM_USER_DISCOUNT_RATE
, and TAX_RATE_MULTIPLIER
ensures that these values are
treated as exact literal numbers (0.95, 0.85, 1.10). This means they are not just any number, but the specific numbers you set.
If you use as const
for objects or arrays, it will make them readonly. This prevents you from accidentally changing the values.
For example, you will be prohibited from using the push
method on an array or changing a property of an object.
Apply single responsibility principle (SRP)
This is hands down the most important principle of clean code.
A module, class, or function should have one, and only one, reason to change.
— Robert C. Martin, author of "Clean Code"
Ok, so what does this actually mean? It's basically that a file, class, or function should do one thing and one thing only. It's about dividing responsibilities and making sure each function is focused on a single task.
For example:
- A function that calculates the subtotal of an order.
- A function that applies a discount to the subtotal.
- A function that applies tax to the subtotal.
You wouldn't want to have a function that does all of these things (unless it's an orchestrator function).
Breaking the main function into smaller, focused functions (calculating subtotal, applying discount, applying tax) makes each part easier to understand, test, and reuse.
Just take a moment to appreciate how much cleaner this code is compared to the previous version. Now we have small focused functions that do exactly what the function name implies.
This is a good point to stop refactoring as it's now in good shape.
We did apply the strategy pattern here to the discount rates. This is a design pattern that allows you to switch between different algorithms at runtime. This is going to allow us to easily add new discount types in the future - or swap in different discount logic for each user type.
But more on the strategy pattern in another article.
Quick recap: key principles
Here's a quick summary of the clean code principles we covered:
Principle | Why it Matters | Key Action |
---|---|---|
Remove Comment Noise | Prevents outdated info and forces clearer code. | Write self-explanatory code; comment the why, not the what. |
Meaningful Names & Types | Improves readability and self-documentation. | Use descriptive names, reveal intent, follow conventions. |
Eliminate Magic Numbers | Reduces confusion and makes updates easier. | Replace hardcoded numbers with named constants (as const ). |
Single Responsibility (SRP) | Makes code modular, testable, and maintainable. | Break down functions/components into single-purpose units. |
Final thoughts
Writing clean code isn't some mystical art reserved for coding wizards. It boils down to sticking to simple principles that make your code understandable and maintainable. Honestly, the extra effort upfront is tiny compared to the massive payoff.
Think long-term: clean code is your best defense against future headaches. As the graph clearly shows, there's a direct link between code quality and how efficiently you can actually get stuff done.
This means you'll not only ship features faster, like a true 10x engineer, but also find maintaining and extending your codebase way less painful down the road.
By writing code in this manner, your future self and your colleagues will thank you. This is the type of giga chad code that you can be proud of and that will impress colleagues and future employers.