#begin
Today we are going to look at the next chapter of the Clean Code book by Robert C. Martin. In the previous blogs of this series we discussed the first four chapters of the book. We discussed the background, meaningful names, functions, and comments.
The next chapter we a going to dive into is aimed towards aesthetics of the code and not so much about implementation details. It’s about formatting. Now, why would formatting be important? Well, C# code, or imperative code for that manner is always formatted and structured in the same way. If you divert form that formatting, your code will look weird or has not been given much attention. A simple example I can give you is from an applicant that was applying for a senior position. But the code he send in for us to check out was formatted horribly, indents were all over the place and the code just did not read well because of it. Now, someone applying for a senior position should have known this. He’s supposed to be senior for a reason right? So we turned the guy down, sadly enough for him. And we gave him the advise to carefully format the code to match the best practices used in the industry. So, horrible editing, can cost you a job. I guess that’s something you can learn from this story.
So the next chapter in the clean code book will teach you some of these best practices. And as with some of the other content in the book. Some parts are dated but I’ll describe them when we reach these topics. What then does Uncle Bob say about why formatting is important.
Formatting
Well he says that when people look under the hood you want them to impressed by what they see. It’s the same when you pop the hood of your car and you want people to be impressed by the state of the engine. You don’t want them to frown their eyebrows and look away in disgust. We want them to be amazed by what they see and start throwing flowers and gifts or whatever. They want to be struck by orderliness.
So you should chose a formatting style that is applied consistently across all of your code. And preferably, use a tool to do it. Our IDE’s have support for this out of the box. The first thing I do when I open a file in rider is smack CTRL+Alt+L for core formatting. But you could also chose to like hook up some linter in your continuous integration process or something. A linter is a piece of software that formats your code to a certain set of rules. A simple example is a JSON linter that will indent everything properly, you have probably used some kind of JSON formatter before.
So formatting is really important. Code formatting is about communication. I mean if you smack all your code in one line the compiler will be perfectly happy to swallow it and compile it down to intermediate language code and in the end machine code. It does not care about line break, just semicolons right. So formatting is about readability and communication which is very important to you as a professional game developer. Maybe you always thought that getting shit working was you job, but I hope after all this talking you start to understand that making things readable and communication is a big part of your job as well. Maybe even more so than getting things working.
Uncle Bob starts with the concept of vertical formatting, which is directly related to the size of a file. Let’s just start by saying that probably everyone gets annoyed instantly when they very large class. Some might say a 1000 line file is far too long while others don’t care about it and start to get annoyed at a point when a file is 5000 lines for example. Me personally, I like files, or classes for that matter as small as possible. I would consider a 500 line class very large, and yes, I might have a handful of these “large” classes in my own code-base for things that aren’t worth separating like some kind of service classes. Although I would extract their functionality out to use-case-like classes most of the time any way.
Use-case classes are a concept from Clean Architecture, also An Uncle Bob book. Use-cases are wrapper objects for application dependent business code. So use-cases, often referred to as interactors in clean architecture are classes that embed entire features or requirements like: Create order. The concept of these types of classes are taken from a book called, Object-Oriented Software Engineering, By Ivar Jacobson and it’s subtitled “A use-case driven approach”. It’s a true classic in software design and development and surely a must read to anyone who wants to take their skills to a new level. I also wrote a review of the entire book on my blog. I’ll put a link to it in the shownotes.
Sorry, I went of a little tangent there again. But.. Yeah, a file should be at most 500 lines, and these classes should be really, really rare. I can’t really put a number on it since, 0 large classes is the best of course, so maybe try to keep them as close to 0 as possible. Then another thing, since we are talking about C# here, and all functionality is wrapped in classes. Always, and I mean, there should never be more than one class in a file. So don’t make 1 source file that contains 3 classes. And in Unity’s case, if you put multiple class declarations into a single file that derive from monobehaviour you won’t be able to add them to your gameobjects since Unity has not generated .meta files for them. Same goes for when your class name differs from the file name. So keep an eye out for that as well.
Next Uncle Bob addresses the newspaper metaphor again. I explained this in the previous episode a bit but I’ll do it again here quickly. The newspaper metaphor is the concept that your class should begin at a high level of abstraction. And the further you read down the class, the lower the level of abstraction becomes. This way, at the top of you file you can quickly find out what it is about, and if you need to know the details you can read further. This correlates to how newspaper articles are written. An article always starts with a catchy title, and some abstract with high level information. While you reading the article, more and more details are given until you reach the end. This is polite. You can stop reading until you are bored and still get some valuable information. Classes need to be structured this way as well. It’s polite to your colleagues to write classes this way. And we have probably all seen classes that jump all over the place. So when you need to read things, you need to scroll up and down constantly. So what do you do? Well split the file in multiple windows and dock them in your IDE. Or copy paste method calls around. The same goes for files that contain regions for specific concepts of the code. This can lead to really unreadable classes as well. So apply regions carefully. We discussed this a bit earlier as well.
Then Uncle Bob describes the concept of vertical openness between concepts. So what the heck is this. He states that most code is read left to right, top to bottom. He also says that many coding standards have things in there that separate certain code from each other. What he means is that you most likely start your class with all the using statements. Then, there is a white-line, then a namespace and a opening bracket below it. Next a class definition and again, an opening bracket. Next, a white-line and a couple of fields and/or properties. Then a white-line, and maybe a constructor or an awake.
I think you get it now right? Uncle bob means that these white-lines are there for a reason. When you remove these empty lines, the code becomes to crammed together and unreadable. It will compile perfectly fine but for human eyes it horrible to read. A thing I also often see is if-statements on a single line. I’ve done this many times as well. You probably also. You know, just an if-statement, without brackets on a single line as a simple guard in the code. But I’ve found, over the years that putting this on two lines is better. It makes reviewing code much easier for example. I mean, sometimes, when you have too many concepts on a single line you can create like an optical illusion of weird conditions and function calls. So you might not spot a bug or simply a fault in requirements. So my advise is to make if-statements at least two lines, one for the condition, and the second for the action in the body.
This was a bit of a tangent but still kind of a segway into the next topic in the book which is what uncle bob calls vertical density. And Vertical density means that code that is association with each other, should coincide with each other. A simple example for this would be if you have a nice compact little lambda function which can be 1 line, but instead you spread it across multiple lines. If the code belongs to the same concept, try to make it compact, yet still readable. It’s a bit like getters and setters in C#. We can write the out like true encapsulated fields, or we can rely more on the compiler to do it, and use auto properties. I think, nowadays you will mostly see auto properties in C#, and no longer the large clunky, bulky encapsulated fields written out. But if you were a Java programmer, then you would still need to write this cumbersome getter setter logic all the time.
Next there is the concept of vertical distance. And Vertical distance refers to the distance between the usage of concepts. Uncle bob says that you should define or declare variables where ever you use them. So don’t write a 100 line function, put some variable declarations at the top and then use one, or more of them only in the last 10 lines of the function. Just move the fieds down to the part you use them. And there might be some exceptions like declaring variables just outside a try-catch block for example, so variables might seem unrelated. Sometimes you need to do that. But in our previous episode we talked about how we should keep our functions small and also, if you are working within a try-catch block you should keep the body of the try-block as short as you can. So if you do that the vertical distance between concepts shall never be large.
Then we probably all know about always declaring your instance variables, and properties at the top of your file. I mean, this is a greatly accepted standard. Always, are variables and fields declared at the top of the file. If someone diverts from this, and declares fields at random positions within a file, like between functions, you quickly notice and find that readability suffers.
And Uncle Bob also has a rule for function declarations and ordering. He says that the functions that are related to each other should be close to one another. With this, he refers to the news paper metaphor again. And what he means is that you might have like a publicly exposed function, then a couple of private functions that are called from within the public function. And after these private functions another public function might be defined followed by a bunch of private functions that are called from within the second public function. This way you can easily read the class. This also refers to the concept of vertical ordering where we want functions to be declared in order as they are called so you don’t end up jumping all over the place while reading it. We glanced at this topic a bit earlier already.
The last concept of vertical formatting Uncle Bob talks about in his book is called conceptual affinity. And this refers to some logic really wanting to be close to each other. That sound weird I think, I mean how can code want such a thing, right. What he means is that there might be functions that conceptually, refer to the same things or work with similar concepts. So if you have a class called player which has functions for moving, shooting, and jumping. Group together the functions that have to do with moving, jumping and shooting closely. Don’t have like a function that moves the player left, then a function that has to do with shooting, then something that has to do with jumping and then a function that moves the player right. Group these functions close to each other if they share the same concepts.
Alright, so we have discussed everything Uncle Bob has to say about vertical formatting. We talked about why vertical formatting is important and why it should follow a news paper style of writing and concepts. We discussed vertical openness, density, distance, ordering and conceptual affinity. But there’s also horizontal formatting of course.
Horizontal formatting mostly refers to the length of lines in classes. And we all know that the age old advise goes that lines should never be longer than your screen is wide, which was about 80 characters. And since we nowadays have very high resolution, curved, widescreen monitors, you will probably have seen 300 character or more, wide lines. It’s fits on one screen, right? Well let me tell you this. We humans have been conditioned to use computers in a top down manner, I mean we have scroll-wheels on our mice that scroll up and down. And I know, if you have somewhat of a nice mouse, you can shift right and left as well, but generally, we vertically, not horizontal. So, making a developer scroll left and right, because you were too lazy to properly format the code is rude. And personally, most of the time I won’t scroll right and I’ll just refactor the code to multiple lines to fit them better on the screen. And this sucks to be honest, because I might not have anything to do with this particular code. I might just be reading it to grasp a concept, but now I feel driven to refactor it. So try to keep lines short and I know there can be edge-cases where you will reach like 100 or 120 characters for example. I think this is acceptable, but don’t go down the deep end and make lines 300 characters long.
So Uncle Bob has some advise to share about horizontal formatting as well. And he starts of by saying that he analyzed some codebases for line length and found that generally there is about 80 lines long, as the age old rule suggests. Uncle bob says he has set his own character limit in his IDE to 120. I guess this is fine, although, I have tried this particular limit and it can cause some awkward formatting when you have long class or function names, or even functions with more than one generic type parameters. So a hard limit for 120 might be cruel in some cases since you really need to format the code weirdly in specific cases.
But Uncle Bob’s first advise for horizontal formatting is the concept of openness and density again. We discussed this for vertical alignment as well, and where openness refers to new or empty lines n vertical formatting, a simple space will help for horizontal formatting. So you can add spaces to your lines to separate concepts. A simple example is adding a space between operators in a mathematical formula, or spaces between conditional arguments in if-statements, or even parameters to a function. They are separated by comma’s with an additional white space behind the comma. I think our IDE’s all do this out of the box.
Horizontal density refers the basically the same thing as vertical density and that is that code that is associated with each other should reside together. So, try to keep mathematical formulas on a single line, same goes for if-conditions and function arguments. Although in some cases I will put certain if-statements or function calls on a multiple lines. For example, if you call a function that requires a couple of arguments, and you run some magical linq query directly to pass it’s result into the function. Sometimes these things can become quite long, and yes, you and I included should probably first make a local variable to capture that result and pass that variable into the function to reduce the length of the line. This way you don’t need to put things on multiple lines.
Then a topic that brings back some memories is horizontal alignment. And I think we have all seen code that was aligned really nicely with tabs, or spaces for that matter, everywhere. So access modifiers types and names are tabbed out evenly. However, this is far from the industry standard since there should not be horizontal alignment in source file. Text should be compact and dense yet readable. And I said I remember this quite fondly and what I meant was that I was helping or accompanying an intern. And he would always horizontally align his code with tabs. So every single time I helped him with some things my muscle memory would kick in and I would Hit Ctrl+Alt+L in Rider, which reformats code, and his entire class layout would change. I could undo that action but I tried to convince him that his formatting is different from the best practices. In C# at least. And I think, by the end of his internship, he didn’t format his code horizontally anymore. He was a great intern who took advise gladly and did not get “offended” when I criticized his code. We all probably know people who simply cannot take feedback if it’s not what they want to hear. And it may not even be negative. I guess this is some advise to any junior’s listening. Try not to take thinks personally. Try co carefully consider feedback and realize that people criticize code, not the person who wrote it. At least, they should not criticize the person in my opinion.
But, yeah. The next topic to discuss for horizontal formatting is indenting. We talked about this in the previous podcast as well but that was more about structure in functions and that we should try to keep the level of indenting to 1. But in this chapter indenting is more about readability. Imagine if all code in a source file would not be indented and everything would be crammed from the beginning of each line. I even needed to adjust myself when I wrote some back-end logic where I used a newer version of .Net and you can declare namespaces without the brackets and the main or program class without class definition. So use indenting to indicate class and function bodies etc. I think this advise is dated since our IDE’s enforce these things automatically but I think it’s still good advise because it still really matters.
Then he has another piece of advise and that is to not use dummy scopes without braces. But what’s a dummy scope? Well a dummy scope exists when you have for example a while loop without a body wrapped by brackets but just a simple semicolon. So the while loop is a guard in the code but does not have a body that executed each iteration of the loop. I don’t think I have ever seen this in C# code. I mean, I have seen some clever usages of while loops but I’ve not seen it used in this manner. I mean why would you use a while loop for this, there’s better language level constructs to delay or pause your code. But then again, the book is old, and it’s about Java mostly so the java fokes might miss some of our awesome C# or .Net features.
And very lastly, Uncle Bob ends this chapter with what I think the best advise in of this chapter and that is to make rules bout formatting that every single person on the team follows. I mean, if everyone on the team formats code differently it’s going to be a cesspool of weird code. And Luckily we can create these rules and then share these settings among the team. So for example, in Jetbrains Rider you can export the settings file and share it among the team, you can even create a dedicated Git repo where you host the settings and you just connect to it to get the latest ones. So every member of the team can use the same settings and then you can avoid meaningless discussions about formatting. You just hit Ctrl+Alt+L in Rider and code get’s reformatted instantly, and as I said before, muscle memory will kick in really quickly.
So that’s it for the fifth part of this blog. The next blog will cover the sixt chapter of the book called Objects and Data structures. Leave any feedback, comments or discussions below and I hoop you join me for the next blog as well 🙂
Thanks for reading, and see you next time!
#end
01010010 01110101 01100010 01100101 01101110.
Recent Comments