Over the years, I have become very interested in clean code, calisthenics objects, etc. And over time, readings, videos, discussions and katas, my writing of code has changed: less superfluous, more meaningful code (at least for me 😇) and a desire to go to the 'essential. So here is what has evolved in my way of approaching code.
The examples in this article are in PHP, but most of what I describe below is applicable to all languages.
Comments
Commenting on your code can be useful, but it must be done for the right reasons. A comment should say why it was done like that and not what the code does.
// If the user is at least 18 years old
if ($user->getAge() >= 18) {
...
}
This comment is completely useless, it is a literal copy of the if
statement which can surely make you smile. But I used to write this kind of comment and I wasn't the only one (you just had to see the codebase of the project I'm working on some time ago). Look in your codebase, I'm sure you'll find some.
// If the user is an adult
if ($user->getAge() > 18) {
...
}
This comment tells us a little more about what this instruction tests, but I think we can do even better and remove this comment. Many solutions :
- Put a constant in place of
18
```php
if ($user->getAge() >= ADULT_AGE) {
...
}
* Extract the test into a function
```php
if (userIsAdult($user)) {
...
}
...
...
function userIsAdult(User $user) {
return $user->getAge() >= ADULT_AGE;
}
This method is useful if you have a fairly complicated test with a sequence of conditions. Instead of putting a comment, try naming your test and extracting it into a function.
- Ensure that this test is done directly in the object code ```php
if ($user->isAdult()) {
...
}
...
...
class User
{
private $age;
const ADULT_AGE = 18;
...
public function isAdult()
{
return $this->age >= self::ADULT_AGE;
}
}
---
## PHPDoc
PHP is becoming more and more typed, and honestly I've never had to extract the PHPDoc from my projects. From now on for my personal projects, I no longer generate the PHPDoc of my functions. To return to my `User` class:
Before:
```php
class User
{
private $name;
private $firstName;
private $age;
/**
* User constructor
* @param string $name;
* @param string $firstName;
* @param int $age;
*/
public function __construct($name, $firstName, $age)
{
$this->name = $name;
$this->firstName = $firstName;
$this->age = $age;
}
/**
* Indique si l'utilisateur est adulte
* @return bool
*/
public function isAdult()
{
return $this->age >= self::ADULT_AGE;
}
}
Now:
class User
{
public function __construct(
private string $name,
private string $firstName,
private int $age)
{
}
public function isAdult(): bool
{
return $this->age >= self::ADULT_AGE;
}
}
-
The names of the methods are quite self-explanatory, no need to describe them (especially when we see the comments generated by the IDE like
User constructor
!) - The constructor parameters are typed, the IDE can very well indicate the type of each parameter
- The return type of the method is directly in the code
- And the advantage is that if one day a parameter or a return type has to change, there is no risk of forgetting to update the PHPDoc. (because yes, the problem with comments is that over time, they no longer correspond to the code below, due to forgetting to update 😕)
Getter and setter
The IDE can be very convenient, but can also lead to bad habits. Automatic generation of a class's getter and setter is a good example. We don't know if we're going to need them, but we're going to generate them anyway.
I now try to create the minimum possible getter/setter (if not none if possible):
- Passing parameters directly into the constructor. Instead of : ```php
class User
{ // The constructor is empty for the example
public function __construct() {}
public function setName(string $name) {...}
public function setFirstName(string $firstName) {...}
public function setAge(int $age) {...}
}
I do
```php
class User
{
public function __construct(
private string $name,
private string $firstName,
private int $age)
{
}
}
- The object itself can have its own business rules. Taking the example from the beginning of the article on the age of the user: Before ```php
if ($user->getAge() > 18) {
...
}
After
```php
class User
{
...
public function isAdult(): bool
{
return $this->age >= self::ADULT_AGE;
}
}
Temporary variables
Like any developer, I learned to do for
/foreach
/while
loops. And most often this included the creation of temporary variables. If I take the first example from my Refactoring with Collection article, here is the kind of code I used to write before:
public function doubleAllValue(array $numbers)
{
$result = [];
foreach ($numbers as $number) {
$result[] = $number * 2;
}
return $result;
}
We have a temporary variable here. If I use the definition from Wikipedia:
In computer programming, a temporary variable is a variable with short lifetime, usually to hold data that will soon be discarded, or before it can be placed at a more permanent memory location. Because it is short-lived, it is usually declared as a local variable, i.e., a variable with local scope.
And now here is what I usually write:
public function doubleAllValue(array $numbers)
{
return array_map(function($number) {
return $number * 2;
}, $numbers);
}
The use of temporary variables can be practical in certain debugging cases, on a case-by-case basis depending on the complexity of the code.
Early return
During my studies, I was taught that a method must have only one output. But over the years, I realized that it was possible to use early returns to improve the readability of the code.
function canDriveACar(User $user): bool
{
$canDriveACar = null;
if ($user->isAdult()) {
$canDriveACar = true;
} else {
if ($user->learnInAccompaniedDriving) {
$canDriveACar = true;
} else {
$canDriveACar = false;
}
}
return $canDriveACar ;
}
The use of early return will allow:
- Reduce code indentation
- To remove the
else
- To have more readable code
function canDriveACar(User $user)
{
if ($user->isAdult()) {
return true;
}
if ($user->learnInAccompaniedDriving) {
return true;
}
return false;
}
Our practice evolves over the years, sometimes with good ideas, sometimes not so good ones (but we realize it much later!).
What has changed in the way you code over the years? Let's discuss it in the comments.
Thank you for reading, and let's stay in touch !
If you liked this article, please share. Join me also on Twitter/X for more PHP tips.
Header photo by Artem Sapegin on Unsplash
Top comments (9)
I like your approach to the comment section. Depending on the problems, I either use the constant with a name that I can clearly understand or I extract it into a function that I test.
Regarding the "early return" topic, it was one of the first things I learned in the professional world and it really makes sense and optimizes the process.
Thanks for your comment !
About early return, I don’t know nowdays how the « one return only » is teached at school, but it’s a shame to learn early return in the profession l world…
Great article!
side node: I would not let anyone that is an adult drive a car. There are already enough accidents with drivers that have a drivers license :)
🤣
This is good information!
Thank you 🙏
thank you for sharing
Insightful 💯
Nice article!
About the "early return" - most of the time there are more things to check (fail) before reaching the "happy path" and that's why it is usually placed at the end of the method. Your example should looks like: