There is no single definition for good or clean code. The definition will change per developer or team, but one thing the definitions will have in common is that good code expresses its intent properly. When your code looks like the hard to read scripts they show in Hollywood movies, take these tips to improve the readability of your code.
• Keep your variable names short but sufficient, and represent the truth. If you are coding with a collection of cups, this should be your variable name. Avoid using cupCollection or cupList. This is too long and does not read (or pronounce) well in the logic operating on it. When you need to work with multiple variable holding the same type you can separate them like coffeeCups or teaCups.
• For methods that return a boolean start the method name with the prefix Is (or perhaps Are for a collection). Code that uses these prefixes reads easy. if (cup.isFull()) reads easier than if (cup.CheckIfFull()). Do try to avoid negatives like cup.IsNotFull(), for consistency.
• Avoid negatives in boolean methods. cup.IsNotFull() is less easy to read than !cup.IsFull().
• Create methods for checks so your code becomes easier to read, and represents the problem domain. For instance if (cup.WaterLevel >= 10) can easily be replaced with cup.IsFull().
• Similarly, use constants to express domain specific numbers. if (cup.WaterLevel >= CUP_LEVEL_FULL) expresses more intent than cup.WaterLevel >= 10.
• Class names should be nouns, like CoffeeMaker or Kettle. In code you always operate on an instance of the class (which should be named the same) the code will look strange if the class is a verb. runner.Run() or processor.Process() express less intent than kettle.Start()
• Don’t use Hungarian notation or member prefixes. If your class or methods are too large to recognize members, you are likely to be breaking some SOLID principles. When I read code, I pronounce every character or syllable in my head so the prefix in if ( m_Cup.IsFull()) distracts. The code reads easier when it is written like if (cup.IsFull()).
• Method names should be verbs. Verbs operate on an instance of a class. repository.Save(cup) expresses its intent perfectly.
• Separate Queries from Commands through simple prefixes in methods. coffeemaker.TurnOn() is a clear command and expected to change the internal state. It is clear that cup.GetBrandName() does not change any state.
• Avoid prefixes in class names. These days we use namespaces to avoid duplicate names. Sometimes it could be useful to have suffixes to show your reader that your class is part of a bigger scheme, like for instance a State or a Factory.
• Avoid long if constructions and/or switch statements in the main logic of your code. You can generally refactor these out using low-level design patterns (GoF) or adding extra methods.
• Try to keep the number of arguments in your method as low as possible. Passing down 6 arguments distracts the reader from the intent of the code. Although this also goes for constructors, using dependency injection frameworks, this can be acceptable in some exceptional cases. If necessary you can still refactor this into Factory methods that express more intent. Car.CreateMiniCooper() is easier to read than new Car(2000, 4, “BMW”, “Mini”, “Cooper”);
• Less code is generally better than more code, but avoid cryptic one-liners. Do you know what this actual valid C code means? return p??!??!k?p?*p:sizeof(*k):0; I don’t, even after briefly looking at the explanation. Answer here.
• Only add in-line comments when necessary, perhaps to express units of measurement (although a variable name like distanceInMiles would work just as well), or how the calculation works. Generally I consider them distractions, and they are rarely kept up to date. Don’t run the risk that the comment is already outdated.
• Avoid returning null, and avoid passing in null. This will keep your guard statements limited to the constructor. Dealing with null values increases the complexity in the client code, and distracts from the reader. Familiarize yourself with the Null Object Pattern.
• Be consistent in the use of your names, and don’t use the same name interchangeably with a synonym. If in one factory method you use Cup.CreateTeaCup(), you should not use Cup.MakeCoffeeCup() or Cup.ConstructCoffeeCup(). This does not necessarily express more intent, but leaves the reader not guessing if they are different.
Code that expresses its intent properly requires less documentation, and will help the reader of the code understand the code faster. I have mentioned in multiple blog posts that developers spend most of their time reading code – So do your colleague or your future self a big favor. Write code that reads well!
If you have some good tips to express more intent code, please leave them in the comment section below.