If you are interested in reading this article in Spanish, check out my blog The Developer's Dungeon
Two years ago I started looking for ways to improve my coding skills, until that moment I was all on board with learning the next big thing, experimenting with new technologies constantly, and don't get me wrong, I still do, I am a big believer that you should keep your skills updated, but two years ago I read Clean Code: A Handbook of Agile Software Craftsmanship and my career changed forever, I would even dare to say that my view on software changed forever.
After that book, I started really thinking about the code I was producing, how it was an expression of my own self and how important it was for me to take into consideration the quality of the software I was creating.
I am not gonna lecture you on our responsibilities as developers, I will do that on the following article, so let's begin with a little introduction.
What is Clean Code?
There isn't only one definition, there are probably as many definitions as developers in the world but a few key personalities in our industry have their own opinion and I am gonna list here the ones I like the most:
"Clean code always looks like it was written by someone who cares. There is nothing obvious you can do to make it better."
-- Michael Feathers
Michael Feathers is right on point, when you see clean code is code that it has been written by someone who deeply cares about his craft, he/she has considered every possibility, written tests for it and made it easier for the next person working on it.
Clean code is simple and direct. Clean code
reads like well-written prose.
-- Grady Booch
Grady Booch focuses on readability, reading clean code is very easy, expressive and even beautiful. Notice how he didn't mention the writing of code.
You know you are working on clean code when each
routine you read turns out to be pretty much what
you expected. You can call it beautiful code when
the code also makes it look like the language was
made for the problem.
-- Ward Cunningham
This is a great definition, when was the last time you looked inside a method or module and the code was "pretty much what you expected"? it is usually the other way around, weird names, old comments, hidden responsibilities.
Clean code can be easily read and enhanced by a
developer other than its original author. It has
unit and acceptance tests. It has meaningful
names. It provides one way rather than many
ways of doing one thing. It has minimal dependencies, which are explicitly defined, and provides a clear and minimal API. Code should be
literate since depending on the language, not all
necessary information can be expressed clearly
in code alone.
-- Dave Thomas
Dave in comparison with the other authors is much more pragmatic, it clearly states that clean code should be easy to read, should have tests, a good naming strategy among other things.
This article relates more to the last definition, I am gonna give 5 things you can start doing today to improve your code and start walking the path of "Clean Code"
1. Write Meaningful Names
There are only two hard things in Computer Science: cache invalidation and naming things.
-- Phil Karlton
You might be thinking, naming things hard? what is this guy talking about, well naming things is actually pretty hard if put some thought into it, remember what Grady Booch said, "Clean code reads like well-written prose" if you want to achieve that you will have to follow certain constraints and try to name variables, methods, and classes as expressive as you can.
- Classes and objects should have noun or noun phrase names like Customer, WikiPage, Account, and AddressParser. Avoid words like Manager, Processor, Data, or Info in the name of a class. A class name should not be a verb
- Methods should have verb or verb phrase names like "postPayment", "deletePage", or "save". Accessors, mutators, and predicates should be named for their value and prefixed with "get", "set"
- Don't abbreviate names
- Don't use magic numbers
Pick one word for one abstract concept and stick with it. For instance, itβs confusing to have "fetch", "retrieve", and get as equivalent methods of different classes. How do you remember which method name goes with which class? Likewise, itβs confusing to have a controller and a manager and a driver in the same code base. What is the essential difference between a DeviceManager and a Protocol- Controller?
Use variables or methods to describe expressions
if (person.age < 18 || person.driverLicense == null) {
arrest(person);
}
// OR
const isUnderAgeOrNoDriverLicense = pperson.age < 18 || person.driverLicense == null
if (isUnderAgeOrNoDriverLicense) {
arrest(person);
}
2. Write Small Functions, Classes, and Modules
FUNCTIONS SHOULD DO ONE THING. THEY SHOULD DO IT WELL.
THEY SHOULD DO IT ONLY.
I know what you are asking yourself, what do you consider "one thing", generally I consider one thing as Uncle Bob explains it, "One reason to change", if the code inside that function, class or module can change for different reasons, then it is doing more than one thing. The reason we write functions is to decompose a larger concept into a set of smaller steps. If a function is retrieving data, mapping it, composing a different object then all those different responsibilities should be separated into different abstractions.
Doing this also will help to separate the logic for unit testing does components.
This concept is deeply related to the "Single Responsibility Principle" which I will probably look further into in another post π
3. Avoid Side Effects
This is a concept that I am very fan of lately since I have been studying functional programming but basically means that your code should not modify or depend on state outside of its control.
Your function promises to do one thing, but it also does other hidden
things. Sometimes it will make unexpected changes to the variables of its own class.
Sometimes it will turn the parameters passed into system globals. In either case, they are devious and damaging mistruths that often result in strange temporal couplings and order dependencies.
As I explained in my article about functional programming that you can read here you should try to stick as much as possible to "Pure functions", functions that for the same input, always produce the same result, by doing this you will isolate the behavior that produces side effects (because we will want to save to database or call an API eventually) from the code that is "Pure".
4. Don't Repeat Yourself
Also known as the DRY principle, it basically explains that you should avoid duplication.
Duplication may be the root of all evil in software. Many principles and practices have been created for the purpose of controlling or eliminating it. Consider how object-oriented programming serves to concentrate code into base classes that would otherwise be redundant. It would appear that since the invention of the subroutine, innovations in software development have been an ongoing attempt to eliminate duplication from our source code.
I know it pretty obvious but sometimes we don't realize that duplication also comes in the sense of information, not only duplicated code but also by providing multiple ways of doing the same thing.
Without use giving some conscious thought into the ways of implementation we provide, we can very quickly have a class with multiple methods all for similar purposes, all with different implementations where bugs can hide, bugs we will have to fix and code we will have to maintain.
5. Comments Are Lies
This is a controversial one, I usually say that you should not write comments at all. Every time I mention this, people love to criticize me.
Before you jump to my neck, hear me out.
In general, I believe that comments are unnecessary, we developers use comments as an excuse for not properly making our code understandable. We create a big mess with dirty abstractions, bad names and hidden responsibilities and we try to compensate by writing a code comment.
A common fallacy is to assume authors of incomprehensible code will somehow be able to express themselves lucidly and clearly in comments.
-- Kevlin Henney
Worst, in some cases comments are indeed lies, they were written ages ago by a developer that no longers works in the team, but no one dares to delete the comment in case it is still important, this way the comment actually gives wrong information about the current implementation of the method.
So I have the following recommendation instead of using comments:
Explain yourself in code
// Check to see if the employee is eligible for full benefits
if ((employee.flags & HOURLY_FLAG) &&
(employee.age > 65))
Or this?
if (employee.isEligibleForFullBenefits())
Again, in most cases, if you write a comment, that is a signal that you should be focusing on extracting that function, giving it a proper name or using some other refactoring techniques.
But as always, there are some exceptions:
- If you are writing a public API, code comments could express intent and documentation for some automated tools like Swagger
- If you are writing code that needs to be extremely performant, usually means handling very low-level stuff and using little abstractions, in that case, a code comment can be useful
- Sometimes there are legal reasons to include comments in our code
6. Write Tests
Wait, didn't you say five? sue me. This one is so important that I am gonna give it to you for free. Still, even though it is key for our industry I am still surprised when people ask me what is the purpose of writing tests.
They tell me stories of how they work on a greenfield project where the code is great so they don't see the benefit. If you don't have tests, then is not a greenfield project anymore my friend, or as Michael Feathers puts it:
Legacy code is code without tests
Tests will just slow me down, they say.
I have even worked with people that they decided to comment out all the failing tests in the project so they could meet a deadline with a redesign.
Let me be clear about this, if you don't have tests, you don't know if your code works. If you are pretending you know your code, you are assuming you are delivering the specified product. There is a lot of fear right now in the world because of "Corona Virus", would you use a new vaccine if it hasn't been tested yet? I bet you wouldn't, but you are very comfortable delivering software that it hasn't been properly tested.
That behavior and recklessness had lead to lives and millions of dollars lost. Some examples are:
This is a topic that is very dear to my heart. Since I read the book, a book I recommend every developer should read, in case you are interested you can read my full list of books here, I have started looking at my career in a very different way.
I am a Software Craftsman and I will try to show the love for my craft with every single line of code I write.
It is not an easy task, but I actually have a picture of uncle bob pasted to my monitor judging if I don't write clean code.
If you liked or didn't like my article please let me know in the comments below, if you have any questions you can also comment or contact me on twitter, I would very happy to help you π
Lastly, if you really really liked it please share it with others.
Top comments (13)
Regarding comments. 100% agree with Patricio. More over - Gentleman's! The case has been closed for 12 years! Rober C. Martin made it clear in his book - clean code. The code should be self-documenting. This means that "no comment" should be the default - if necessary add the comment to express your intent or if you need additional license info to a script file (for projects you can include it in assembly info). But this is rather an exception, not a practice. The intent of the developer if it cannot be included in variable name, method name (in most cases it can) then there is a far better way of documenting the code than a comment which WILL (sooner or later) become falsy - write test that explains the intent. Also, take a look on Mark Seeman tweet:
I heard you out, but I still disagree with your stance on comments. The entire case falls apart if comments are dedicated solely to the one thing they're appropriate for: stating intention. The cleanest code in the world is not going to tell you anything about what the programmer meant to do.
Everything you said is true of comments that restate code, and there is certainly no excuse for those sorts of comments. But just because polka is annoying doesn't mean we burn all the accordions.
(And yes, I've used intent-commenting for years with my team; the short- and long-term benefits greatly outweigh the effort involved every time.)
I've also noticed that intent comments doesn't get stale like bad comments, intent comments are tied to the purpose/objective instead to code, is not rare that a piece of code change but not that it's purpose change.
Maybe that's a sign of a useful comment, one that shouldn't get obsolete with an implementation change.
I am not sure about this one, I have seen comments get stale even if they are the "good comments", I have seen comments trying to convey intent on code that doesn't exist anymore, it was the intent for a class that it was deleted ages ago. Maybe I had some bad experiences and this is not the norm.
The only reason it gets stale is if you let it. I hear the "out of sync" argument a lot, but that problem is just as common with descriptive names! Yet I don't see any articles decrying descriptive naming as a result: you update the name.
Keep your comments in sync as a mandatory part of refactoring. Get into the habit of distrusting any code with a mismatching intent comment; it usually means both code and comment are wrong.
If your comments usually get out of sync, then it could be a symptom your code review process needs to be improved.
I do agree with you, maybe the rule should be then, only write comments to provide intent? unfortunately, this reason is not the main reason people use comments, at least not in experience. People tend to use comments to restate code or explain bad implementations, intention comments are just a small part of that.
I feel like it is another exception, and not something big enough to change a rule.
That's when we enforce intent comment.
People abuse lambdas, but I don't hear any calls to eschew them from code.
To Comment Or Not To Comment?
Jason C. McDonald γ» Jan 20 '19 γ» 12 min read
I disagree with your stance on comments a bit. I agree that comments should not be explaining what the code is doing (unless it's really necessary to write some hard to grok code for the sake of performance). However, comments are useful in explaining one thing that code alone cannot - why you did something.
One example - If you made a decision based on tradeoffs, especially if it's not the obvious choice, and you need to relate to the next dev that they shouldn't refactor it to the "obvious" choice, comments are a great way to keep that note right by the code.
But mostly I agree that code should be clear enough that you shouldn't have comments explaining what your code is doing.
Hey Jeremy, thank you for commenting. I have to agree with you on that regard, that one is a good example of a valid comment I did not thought off, but wouldn't you say that it would fit better on my small list of exceptions instead of a common rule? I still think that in general you have more reasons to avoid comments than to use them. Unfortunately most of the comments we see in code could be very well explained with proper naming and correct use of abstractions
While they can certainly be misused, the best, legitimate use of comments is likely to state the "why" of a solution...the business rules or decision points...something that is rarely, if ever, explained by the code itself. It's the question that the person maintaining your code will ask themselves over and over again. So, do them a favor...and explicitly answer it, proactively.
I often find myself writing comments for a line of code which is self explanatory and wondered why I bother writing the comment in the first place.
Been there, done that my friend ahaha π