Writing code is the easy part of programming. Our aspiration is to write clean, maintainable code that reads like well-written prose. Yesterday was the day when I googled "Who is Uncle Bob?". I am glad that I did.
Robert Cecil Martin, colloquially called "Uncle Bob", is an American software engineer, instructor, and best-selling author. He is most recognized for developing numerous software design principles and for being a founder of the influential Agile Manifesto. Martin has authored many books and magazine articles. Wikipedia
I guess most of the developers should already know who Uncle Bob is, but if you are new and you have not heard of him yet, he is the author of well-known books like "Clean Code". Here is a video (also embedded below) that I started watching last night and could not recommend it enough to anyone who cares about code. The amount of energy in this presentation alone is impressive. He must have been the most enthusiastic person that I have ever known who talks about clean code with such affection. The many takeaways I gathered from this talk are included below but I strongly recommend anybody to watch the video. It's entertaining for its own sake. And, who doesn't want to write clean code?!
Some pointers
- Functions should be actions, hence the name of functions should be verbs, not nouns.
- The code within a function body should be at the same level of abstraction, not oscillating from high level to low level or vice versa.
- One aspect of writing code that is polite is to allow readers of the code to leave early if they choose so and yet understand what is going on. It's like reading a newspaper, people can choose to read the headings and focus on the details, and choose to move on to the next article if they are bored.
- Have variables that read well. For example:
If (isTestable) {}
. Readers understand when the if-statement is executed just from that opening line. - Functions should be small.
- A function that does one thing means no smaller functions can be extracted out from that function.
- If a single huge size function involves a bunch of local variables and a bunch of methods that manipulate them, it is probably a good idea to turn it into a class.
- Imagine if the body of if-statements only contains a function call, it will be easy to understand what is going to happen should this branch execute.
- Function arguments should be no more than 3. If there is a valid reason to have more, it might be wise to create objects to group those data.
- Usually, don't pass boolean into a function, just make two functions for the two truth values.
- Write code that does not make readers double-take: a line of code in the middle of the function body that is hard to understand and forces readers to re-read the entire piece after finishing it.
- Avoid switch statements. It is harder for the addition of new functionalities or objects. To obey the open-close principle, use a "base class + derivatives" structure that is better for future expansion.
- Separate GUI code into modules away from core logic because GUI changes most often. To independently deploy different modules means we can avoid having code changes for GUI breaking the entire code base.
- Typically, functions that return an object do not change the state. On the other hand, functions that just command and returns nothing will change the state and cause side-effect.
- Functions that handle exceptions should only handle exceptions. For example, a function that contains only the try block, and within the try block it calls a function that might trigger the exception.
- How to get rip of similar duplicated loop structure? Have a function that contains the loop structure and accept lambdas that does different things.
- Science doesn't use experiments to prove what is true. It uses experiments/tests to prove what is incorrect and therefore with enough tests, we call it a day and assume something to be almost true in that context. So, write your damn tests :)
Top comments (15)
Bob is a great speaker and very convincing but in practice much of his advice is really useless. Dependency inversion is an illusion. Just because class A now no longer depends on class B (in the java imports sense) does not mean class A will not fail when class B fails. The interface they both now βdependβ on does not break or change the real dependence. Itβs smoke and mirrors as is OCP. No program can be closed for modification. It can only be closed for a very narrow class of previously anticipated extensions. Sounds impressive, in reality everything can and must change.
I agree with you on two counts:
However, I don't think dependency inversion is supposed to mean that client code will still work if implementer code breaks. Correctly me if I am wrong, the interface is meant to be a contract between the client and the implementer. The contract doesn't do the work. It is used to express what the client expects and what the implementer must satisfy. If the implementer code breaks, we just go out there and get someone else to bid for that contract.
Now, it is certainly true that both side of the abstraction barrier change way too often in reality(Or is it ?). I wonder is it really better with/without the concept of contract. Certainly in other aspects of our lives, when things are serious, we always have this concept of contract in place. Do we need them in code? I don't know...
Just my two cents.
I've listened to a few of his talks but most of them are just rants. His books are allright, but really Uncle Bob doesn't even practice his own methods because he doesn't code.
Think before you speak, boy ;P.
github.com/unclebob/fitnesse
Did you even look at the contributions before you posted? I'll give you a hint Uncle Bob isn't the main contributor. Nice try though. Check before you post, boy. ;P.
Dammit! But... he's so convincing!
When you are the maintainer of a repository that doesn't mean you have to have the most commits. That is a fallacy.
Indeed. It's that exact reason why I'm not a fan of final classes and the classic builder pattern: they assume that the creator of the code is able to predict the future with perfect precision.
Disagreed:
final
indicates that the design was not intended for extension.Yes, and when that's part of a framework, future users of the framework can be pushed into a corner because the framework designer e.g. did not intend/foresee a new security approach that doesn't use username and password but 1 string or 3 strings.
When using 'final' on a class you inherently intend to not be future-proof, whether you realize it or not.
You are right. The user is pushed into a corner.
Being future proof should be a conscious decision (because its cost and risks are non-negligible), not the developer's to make.
To me, it's comparable to having a
const
member function/parameter/local that guarantees that my code does what I want it to do.Unless I declare a public class
final
, It's harder for me to guarantee the working of the framework (cf. fragile base class). If I want you to extend behavior, I'll make you inject an interface you can implement.The middle ground is I give you an abstract base class to extend. Consciously.
Especially when designing bigger systems, dependency management becomes more important. He did write some good things about architecture, too.
blog.cleancoder.com/uncle-bob/2012...
I like to ask you something about the mechanics of dependency injection?
in dot.net, you configure what implementation to use and inject in a
Startup
class. in testing or different environment you can use different implementations of thestartup
class and thus choose the implementation. this is to have some dependency configuratiob.in java spring framework there js a dependency injection configuration as xml.
in angular with typescript, there are modules fikes within the folder structure.
an other approach, other then using a framework, is to do dependency injection manually. some code know how to wire this app together. or even dependencies get passed into functions,... basically DI all over the code.
In JS for testing it is also pissible to use modules like
rewire
, that change the implementation of therequire
method to provide a mock into the module, sometimes used in testing.what are your opinions on these mechanics? do you prefer it more abstract in a framework, or more direct with more hand work?
I do you one better, Where is Uncle Bob? π
Uncle Bob
in my country is a person who selling Fried Chicken Chop in a RV.