Hi there, fellow readers! It’s been more than a month from my latest commit, where I promised this post will be published a week after 😅 Sorry for the delay! I got my hands full for a month 😓
As I said before, the Clean Code book got tons of useful practices. In this post, I want to show you how I applied a few of them in my code – which mostly is Objective-C, hence the examples on this post 😉 I believe I don’t always get it right either, so I’d love to hear from you if I got something wrong on how I applied it! 😁
So, here’s the top seven clean code practices I mostly use! 😆
1. Use Comments Where Appropriate
Programming languages (most of the time) provide ways for us to write words that won’t be compiled by the machine, which were known as comments. We normally use them to communicate with other developers when our code fails to do it, such as:
- Giving legal and authorship information of the written code,
- Writing TODO notes on parts that need to be enhanced on next iteration, or
- Giving emphasis why an algorithm/decision was taken to tackle an issue.
Alas, there’s no one stopping us to use comments inappropriately – which is using it as our primary way to convey message to others, instead of our code. Here’s an example code of inappropriate use of comments:
(It might be hard to read – even I have a hard time intentionally writing code this bad 😓)
The above code generates this simple view:
Now, let’s take a look at the code. The comments in it were used to:
- Add limiters between chunk of code (the top most view, title, and bottom view),
- Explain what does the variable used for, and
- Storing a piece of code by commenting it out.
Huh, those comments turns out to be helpful, no? Not really. The only way to make sense what is ‘both’ variable in line 39 (the ‘copy’ variable declaration, doh) was to read line 30‘s comment, which was bottom view’s height. There’s a good chance that we’ll waste our time scrolling through the code to make sense what does each variable do when we’re fixing a bug or adding another feature (e.g. another view below the blue one). Rest assured, there’s a better way to this! 😉
To make this code better (read: clean it up), we need to remove all the comments (except the section-separator ones, for the sake of this post 😁). Yep, the commented-out code is included, since we can easily revert it back using version control software such as Git. After removing all those comments, we need to use meaningful names for our variables.
2. Use Meaningful Names
After all of those comments removed, we need to use variable names that describes what kind of data the variable holds. That way, we’ll still be able to make sense out of it without the comments. Here’s an example:
Now, we can directly know how copyright label’s Y position is calculated in line 39 – without all the unnecessary scrolling! 😉
Just like variables, concise and meaningful names also needed for functions and classes. A good name will tell the purpose of a function or class without forcing the readers to re-read the implementation. This simple practice is important – important enough, since the book has a dedicated chapter to about it. Alas, coming up with a good name isn’t always easy. There’s a quote regarding two hard things in computer science from this post:
There are only two hard things in Computer Science: cache invalidation and naming things.
— Phil Karlton
(It seems Mr. Karlton passed the CS classes with flying colors, no?)
Take heart, though – a good dose of knowing your project’s domain, reading good naming samples, and a little experiment here and there will help you better at it! 😉
3. One Level of Abstraction
On our latest code, we have ‘viewDidLoad’ method that executed when the view is loaded to memory. If we narrate the current code, it would be something like this:
When this class’ view loaded to memory, it will call the superclass method first. Then it will create a top view with same width and 100 points high, at (0,64). It will set the new view’s background color as red. Then, it will create a title label with 50 points wide and 100 points high, at … etc.
While it’s perfectly fine to load another view on this class’ viewDidLoad, we got a problem. What if we need to call the retrieveData method (that was removed before) on viewDidLoad? It will work just fine, of course. But it wouldn’t be so pleasant to narrate:
… Then it will create a top view same width and 100 points high, … and finally, it will check if current data is stale. If it is, it retrieves data from server.
The method itself will also be harder to read, since it has multiple level of abstractions. At some point, the viewDidLoad method code has fine details about creating the sub-views, and suddenly a more abstract code about reloading data. And folks, that’s when functions (or methods, in this case) come to the rescue! 😆
To make the viewDidLoad method only has one level of abstraction, we need to move each section of code (which marked with limiter comments) to their own methods. Here’s an example:
On this version, the viewDidLoad method is easier to be narrated:
When this class’ view loaded to memory, it will call the superclass’ method first. Then it will configure view-related properties, then add top and bottom views. Finally, it will validate any stale data.
The methods that called by it should be easy to be narrated, too! You can try it by yourself 😉
4. Reveal Temporal Couplings
Now, imagine that we’re working on a team. There’s another developer that merged his/her feature git branch and merged it to the develop branch. After several merge conflicts, the ‘[self addBottomView]’ called before the ‘[self addTopView]’, and they (hypothetically) doesn’t bother to check how it does:
They thought that everything is gonna be alright, since the methods will add the top and bottom view regardless on their call sequence. The problem is it isn’t – here’s the result if we run the code:
The cause of this mess was ‘addTopView’ method that increases Y offset variable after it added the top view. This creates a temporal coupling, where addTopView should be called before addBottomView . Temporal coupling itself isn’t bad, but it gets bad if the dependency is not mentioned explicitly. To fix this issue, we can add parameters to addTopView and addBottomView method:
On that latest implementation, we don’t have to worry about adding top or bottom view sequence juggled again, since the dependencies is declared explicitly through the method arguments. It also reveals that bottom view’s position depends on the top view’s height, which is the temporal coupling in this code! 😉
5. Reduce Number of Function Arguments
From the latest example, we saw how function (or method) arguments could work as dependency declarators. Yet, having a lot of arguments is never a good idea. Uncle Bob said that:
- No arguments (niladic) is the most ideal,
- One (monadic) and two (dyadic) arguments are the second and third best,
- Three arguments (triadic) should be avoided where possible,
- More than that (polyadic) requires very special justification.
Number of arguments matters because it increases abstraction – it’s easier to use functions with less arguments, since we didn’t need to know the related dependencies. I normally found functions with massive parameters when somebody trying to jumble-up a lot of things into one, e.g. a network request with multiple parameters like below:
This method is hard to use, since the user need to know a lot of dependencies. Plus, it will be hard to test, since there’s a lot of combinations that could happen. How should the code behave if ‘address’ passed as null value? What if all of them are a null values? There are so many edge cases that should be validated in this method. To fix this, we should move all the parameters (and validation) to a model, for example:
That way, we can leave the parameter checking and payload creation to the RPSUser class, and just use the payloadDictionary on the networking method. Plus, it’ll easier to test since the edge cases for creating parameter was moved to the model class! 😉
6. Do One Thing
Phew, we discussed a lot with code samples, no? Now, I want to discuss about practices that’s more philosophical – “Do One Thing”. Originally, this concept was introduced by Uncle Bob for functions, per this quote:
Functions should do one thing. They should do it well. They should do it only.
This principle matters because it assures that a function do only one thing, without any side effects. The “copyright label not found” issue point #4 happened because the ‘addTopView’ method does more than one thing – it adds top view and increases the yOffset property. In a way, it deceives those who use it since it does more than what stated in the method name.
On the later chapters, I found this principle resonates in classes (Chapter 10), but in a different name – responsibilities. A class should only have one responsibility. There are some ways to check if a class has more than one responsibility:
- There’s a conjunction word (e.g. and, or, but) when we explained what’s the class for. For each conjunction, it got one more responsibility.
- The class name includes weasel (misleading) words like Manager, Processor, or Super. And oh – Jeff Atwood also wrote a great blog post regarding this. Check that out! 😄
- The class have too many methods and variables / properties. Having too much of those two could be a smell of too many responsibilities.
- The number of other class that imported. Unless its a Factory / Builder / Provider class, a long import line could be a smell of too many responsibilities, too.
Having a single-responsibility class helps us to maintain it, since it only have one reason to change. It will result in small, cohesive classes that easier to be changed – instead of a brittle thousand-lined class, where a single change in one function will create a bug in another. 😱
That being said, on the latest ViewController sample, we could move the view-creation code to a single UIView subclass. We could apply this if the view got more complex, so the view class will handle how it will layout itself. Later on, we can simply use it on the ViewController’s loadView method! 😉
7. The Boy Scout Rule
And we’re on my top 7th clean code practice, the boy scout rule! Putting it on my #7 does not mean it less important than the others – it’s just I need to practice it more. This rule was summed up in this quote from The Boy Scouts of America (which was taken from the book):
Leave the campground cleaner than you found it.
This rule was present because people not always write clean code. As for me, finishing the Clean Code book doesn’t mean I always write one. I always strive to do so, though.
There are times when I lumped one big bugfix to a single function on deadlines. Or wrote a big ball of mud because I didn’t know the common design patterns of a new language. When I voluntarily not writing clean code, I put a TODO comment and revisit it later to clean it up. Since I’m not the only one who applies this principle, sometimes my TODOs were cleaned up by someone else, and vice versa, too! 😉
Another reason why this principle is important is to prevent “broken windows”. Thanks to entropy (if you agree with physics), we tend to make the code worse if we left it unclean. In my junior years, I tend to put a lot of logic code in UIViewController subclasses since others also did it 😅 When we clean up our code frequently, we set a good path to keep it clean for those who will maintain the code after us.
Phew! Well, that’s my top seven clean code practices! It turns out that this post has become longer the last one 😐 I hope this
post commit turns out helpful for you! I’d also love to hear your comments (and your favourite clean code practice) on the comments section! 😉
Thank you for reading! 🙂