Clean Code - Writing Code for Better Readability
"Any fool can write code that a computer can understand. Good programmers
write code that humans can understand"
- Martin Fowler
I used to think that Clean Code is such a subjective topic until I worked on a code written and refactored by hundreds of people only to realize that it's not all subjective. Clean code is not a myth, it's very real and necessary. In my search for some good
resources, I happened to stumble across this course which I definitely recommend every one - 'Clean Code: Writing Code for Humans' by Cory House on
Pluralsight. This course is a summarized version of Robert C. Martin's (Uncle
Bob) a very famous book called 'Clean Code'. It has good content, code examples, and demos. I will share a few key points which I jotted down while diving into the course. Some of which I knew already, though it really triggered me to actively think about the way I write my code.
"Programming is the art of telling another human what one wants the
computer to do"
- Donald Knuth
Reading and maintaining someone else's code or even your old code gets easier and that's exactly why we should care about writing Clean Code. It is indeed the foundation of advanced coding techniques such as solid code, automated testing, refactoring, etc.
Three principles of writing Clean Code -
1. The right tool for the job: Stay native! Thumb rule - use one language
per file, leverage libraries and watch out for boundaries between
technologies. There could be many exceptions to this but in general,
staying native helps for better readability and compile-time error
tracking.
2. High Signal To Noise ratio: Signal To Noise (SNR) measure
is primarily used in Electronics Engineering that compares the level of
the desired signal to the level of background noise. As a Software
Developer though, our intent (signal) should be clear and noise-free for better
understandability.
High SNR logic follows TED rule -
- Terse - code shouldn't be wordy
- Expressive - code is clear to understand
- Do one thing - Do one thing and do good
DRY principle (Don't Repeat Yourself) comes in
handy to follow TED rule. I am convinced with 3 strike rule which
basically means, if you are writing the same lines of code 3 times, make it
a function and reduce the lines of code.
3. Self-documenting: Ideally, no additional documentation should be necessary. Comments are good, but they should be limited. Programming style, clear intent, layers of abstraction, formatting, favoring code over comments can be really useful.
"Understanding the original programmer's intent is the most difficult
problem"
- Fjelstad & Hamlen 1979
Now, let's discuss how to actually start writing Clean Code:
1) Naming:
Class:
Poor naming creates magnet classes, the common classes lazy developers use instead of using as per the functionality.
Examples - MyFunctions, Utility, Common.
A
well-designed class should have single functionality that name should reflect. Examples - User, Account, QueryBuilder, ProductRepository
A class should:
- be a Noun (not a verb)
- be Specific
- have Single Responsibility
- Avoid generic suffixes - Product (not ProductInfo)
A similar philosophy applies to function names.
Poor function name examples -
get(), process(), pending(), start().
Clean function name
examples - getRegisteredUsers(), isValidSubmission(), sendEmail(),
importDocument()
Naming Warning sign:
Using if/or/and inside method names - means you are doing two things, needs separation. For Example, function checkPasswordAndLogin() should be split into
checkPassword() and login()
Watch for side effects:
One method shouldn't do more than its defined functionality. For example,
checkPassword() shouldn't log out a user.
Don't use abbreviations:
Simple example - class RegisterUser vs RegUsr. Little bit extra storage won't
hurt, its not the 80s
Variable names:
Try to be symmetrical while naming variables.
Poor: on/disable, quick/slow, lock/open
Clean: on/off, fast/slow, lock/unlock
Poor: on/disable, quick/slow, lock/open
Clean: on/off, fast/slow, lock/unlock
Booleans:
Poor: open, start, status, login
Clean: isOpen, loggedIn, isActive, done
Clean: isOpen, loggedIn, isActive, done
Rubber Ducking:
If you are having a hard time naming method/class, it often helps to verbalize, explain out loud what the method/class does. Keep a rubber duck and explain it to him. (pair programming helps as well if you think rubber
duck idea is kind of weird)
Summary:
Remember the following conventions while naming -
- Specific class names
- The name should say it all
- Side effects
- Booleans should sound truthy and falsy
- Rubber ducking/Pair programming when struggling
2) Conditionals:
Boolean comparisons:
When writing conditional statements, it's important to take
consideration of a reader and use the right tool for the job.
Boolean comparisons:
Example - use if(loggedIn) instead of if(loggedIn == true)
Boolean assignments:
Try declaring and assigning a boolean variable at once based on conditional. Remember the DRY principle?
Positive Conditionals: !isNotLoggedIn vs isLoggedIn. Spot the difference? Boolean variables shouldn't sound with negative intent.
Ternary elegance:
Use ternary operators but don't use multiple ternaries as it hurts readability.
Strongly Typed:
Use enums instead of String comparisons.
Magic numbers:
Do not use any magic numbers in code. Always have a variable assigned that reflects the use.
Be declarative:
Use lambda functions, be declarative on what you want rather than writing code
that gets you what you want, just like SQL.
DB table-driven methods:
Most of the ORM frameworks provide you access to a lot of DB driven inbuilt functions. Take advantage of those instead of writing a new function with native queries inside.
Summary:
- Strive for clear intent without leaning on comments
- Be strongly typed via constants and enums
- Be declarative rather than iterative when possible
- consider leveraging the power of DB
When to create a function? If you think there's
- Duplication
- Excessive indentation
- Need to convey specific intent
- More than a single task
Indentation:
According to a 1986 study by Noam Chomsky & Gerald Weinberg,
comprehension decreases beyond 3 levels of nested 'if' blocks.
Extract method:
Return Early, fail fast, throw an exception as soon as the unexpected condition occurs. In that way, big picture readers can only read interesting logic.
Mayfly variables:
Use variables that live for only a short time. Don't go global unless it's necessary.
Catching more than one task:
It's not a good sign if the function has more than 2
parameters and you are scrolling while reading the function top-down. Uncle Bob says that function should have rarely 20 lines and 3
params.
Exceptions:
Unrecoverable - null
exceptions, file not found
Recoverable - Retry Connection, Try different file, wait and try again - ultimately giving up
Ignorable - Logging click (not impacting)
Summary:
Recoverable - Retry Connection, Try different file, wait and try again - ultimately giving up
Ignorable - Logging click (not impacting)
Summary:
- Know when to create a function
- Know when the function is too long
- Act!
4) Classes:
When does creating a class make sense? Create a class if you want to -
- Model the object
- Have low cohesion (methods don't relate to each other)
- Promote reuse
- Reduce complexity
- Clarify parameters
Things to remember while creating a class -
- Place related code together
- Follow the top-down reading approach
- Have multiple layers of abstraction
- Write functions having high cohesion
- Don't be obsessed with primitives
5) Comments:
Hate to say this but comments should be the last resort to document code. They
are useful but should be only used after carefully considering other
options.
Generally, the following types of comments should be avoided:
- Redundant words - comments having the same name as function/class/variable
- Intent comments - comments explaining the intent instead of code. Optimize the code to convey accurate intent.
- Warning comments - commenting for code that needs to be refactored
- Zombie code - commented out code possesses risk and shows hoarding mentality. Use SCM to track code changes, not comments.
- Divider comments - comments to divide sections of code.
- Bloated header, defect log - comments that shows metadata. The use of SCM is more suitable here.
Good comments include TODO comments, Summary comments for class/function that
gives a high-level context.
Remember, Comments are both necessity and crutch so use them wisely!
6) Everything else:
- Guard clauses should be validated separately
- Split the methods for easier bug fixing
- The rule of 7 - Methods shouldn't have more than 7 params
- And so many other things that work for you!
Finally, how do you stay
clean?
- Refactor when necessary
- Set and maintain standards - follow a style guide or create one!
- Code review
- Pair programming - real-time code review
- Boy scout rule - while editing code, make it better than already it is
- Use automated tools such as SonarLint to keep track of cognitive complexities in code
I hope these insights help you clean up your code! If you've reached till the end, thanks for reading and stay tuned for more!
References:
1) Course - Clean Code: Writing Code for Humans by Cory House on Pluralsight.
2) Book - Coders at Work: Reflections on the Craft of Programming by Peter Siebel.
2) Book - Coders at Work: Reflections on the Craft of Programming by Peter Siebel.
Comments
Post a Comment