At lastminute.com group we care about the software we
developed. We also always though that what makes a real professional software developer is how much he/she care about
the software he/she is creating: the approach to the problem, the attention to
details in code, the passion put into creating every single line of code, the focus on the mission to solve his/her
customer problems with your software.
In fact at lastminute.com group we try to follow an
approach called clean code, created by Robert Cecil Martin, known as “Uncle Bob”, and famous also to be the guy that invented the
SOLID principles.
In this blog post I will talk about one of the main principles of clean code: meaningful naming.
As stated by Uncle Bob in the beautiful meme above, this principle is simple: choose your name carefully. But what does it really mean to “choose carefully”? . Choosing the right names means the following thing:
- Names should releal intent. Names should answer why a variable, a class or a method exists, what it does and how it must be used.
- Avoid disinformation. Names should not contains false clues about the scope of a variable, method or class. Taking an example from Uncle Bob’s clean code: “Do not refer to a grouping of accounts as an accountList, unless it is actually a list…So accountGroups or just plain accounts would be better”.
- Don’t use noise words. Adding in the name something like “Info” , “Data” doesn’t give you any value.
- Use pronounceable names. This is simple: use names that humans can pronounce without feeling stupid.
- Use searchable names. Avoid single-letter names and numeric constants that make your components harder to find.
- Avoid encodings. Avoid unnecessary encoding notation, like for example Hungarian Notation in which the name of a variable or function indicates its type.
- Avoid member prefix. Don’t add unnecessary name prefix.
- Avoid encoding interface and implementations. Avoid encoding interface with “I” and concrete implementation with “Impl”. If you really need to encode, do it for the implementation.
- Use noun or noun phrase for classes.
- Use verb or verb phrase for methods.
- One word per concept. Choose a word for a concept and always use it each time you have to refer to it in code.
- Use solution domain names. Your code will be read by other programmers. Math names, algorithm names, pattern names are all good choices.
- Use problem domain names. If no “programmer oriented name” exists, go with names taken from the problem domain.
Let’s see an example to understand the real value of naming classes, methods, functions and variable in the right way. For example, take a look at the following C++ code:
struct pt {
float x;
float y;
float z;
};
struct mt {
public:
float m[4][4];
};
class Obj {
public:
bool act;
mt matr;
std::vector<pt> ms;
};
class GameManager {
public:
GameManager(std::vector<Obj> anObjList){
objList = anObjList;
}
std::vector<Obj> get() {
std::vector<Obj> newObjList;
for (auto currObj : newObjList) {
if (currObj.act && currObj.ms.size() > 0) {
newObjList.push_back(currObj);
}
}
return newObjList;
}
private:
std::vector<Obj> objList;
};
Even if you can maybe get a feeling of what is going, it’s hard to really understand all the details of this code,
and every class, struct is supposed to do and to represent. What are pt
and mt
???? What is supposed to represent
a GameManager
and an Obj
? And the method get
, what wants to get????? . You can see that a lot of
things are a little bit obscure in this code.
We can try to refactor it following the names guidelines we exposed above. Do you think the code will improve in this
way? You can judge it by yourself:
struct Point {
float x;
float y;
float z;
};
struct Matrix {
public:
float values[4][4];
};
class GameObject {
public:
bool isActive;
Matrix transformation;
std::vector<Point> mesh;
};
class Scene {
public:
Scene(std::vector<GameObject> newGameObjects){
gameObjects = newGameObjects;
}
std::vector<GameObject> getValidGameObjects() {
std::vector<GameObject> activeGameObjects;
for (auto gameObject : gameObjects) {
if (isValid(gameObject)) {
activeGameObjects.push_back(gameObject);
}
}
return activeGameObjects;
}
private:
std::vector<GameObject> gameObjects;
bool isValid(GameObject gameObject) {
return gameObject.isActive && isValid(gameObject.mesh);
}
bool isValid(std::vector<Point> mesh) {
return mesh.size() > 0;
}
};
It’s incredible how the code now seems self explained. Each instruction appears more clear in its intents. Each class, struct and method doesn’t need any additional comment/documentation. I think it’s really amazing how some changes to the names could improve the quality of a piece of code dramatically like in our case. I hope that this article will convince you that the names you choose define how much you code is “beautiful” in terms of readability and also, more import, maintainability. Quoting what Grady Booch said about clean code:
Clean Code reads like a well-written prose.
Choosing the right names is the first step to make your code more similar to a well-written prose.