Embracing simplicity in your code
Before I start going into any details in this blog post, I want us to have the same definition of the meaning of "simplicity" and "simple" that I am referring to in this article.
I have created many online video courses over the last couple of years, have given on-site trainings for our clients and gave a workshop at a conference. For all these occasions, I try to have my code samples as "simple" as possible.
Let me give you an example. When I teach people in my package development course about how they can test HTTP responses in their packages, the underlying code that needs to be tested looks like this:
public function getRandomJoke()
{
$response = $this->client->get(self::API_ENDPOINT);
$joke = json_decode($response->getBody()->getContents());
return $joke->value->joke;
}
This is some very "simple" code. And the reason for that is that, in this specific lesson, it's not about how to use Guzzle or how to ensure that the response is valid. It's all about how you can write a test for this.
So in this example I am achieving simplicity, by removing important real-world pieces of the code. I am assuming that you know how to do this properly in your own codebase, since this is not part of the lesson that I am trying to teach you.
And the intentions behind it are all really positive. I want you to be able to concentrate on that one thing that I'm trying to teach you.
Now the problem with this kind of simplicity is, that it leads to people thinking that you should write code like this (which you shouldn't). There is no error handling involved and a lot of things can (and will) go wrong, when you do this in your production applications.
But we know better - real life ain't simple, right? #
So when I say that real life code needs to be different, this leads to us as developers writing more complex code. We start to reject simplicity, as it can not be applied to our real world code that we have to live with every day. We are not beginners - so why should I write code like a beginner?
Real programmers don't comment their code. If it was hard to write, it should be hard to understand. -- unknown
And sometimes this even leads to us "showing off" a little. Look what I've achieved - this massive pile of code. Amazing, right? We pulled off this one trick in our codebase that we are super proud of.
This is an image of the cathedral in Cologne, Germany. And just by the looks of it, you can tell that it probably took a long time to build this. It is very complex and simply an amazing piece of architecture.
Now I went on Twitter and asked around what kind of code you saw last, and how you would describe it.
These are some of the results:
- Messy
- Over-Engineered
- Too much abstraction
- Too verbose
- Unmaintainable
- Overly complicated
- ...
I would say that these adjectives are not the first ones that come to my mind when I think of "simple" code.
So there is a lot of room for improvement here.
But what is simple code? #
Simple code is expressive #
I want to be able to read my code as if it was written in plain english. It should all make sense when you read it.
Simple code is unsurprising #
You don't want to get surprised by code that you see. You do not want to spend minutes reading the code, having to build a map in your head about variable names and what content they might have for this specific if case, when that other condition is false etc.
Simple code is transparent #
Similar to how the "simple" code from the screencasts was missing out information, we do not want this to happen in our simple code. It should not obfuscate anything and be transparent about what it does - and why it does it.
Simple code is fun to maintain #
Yeah, you read that right. Simple code should be fun to read (and maintain). You should write your code, look at it again and think to your self "I want to work with that code in the next 5 years!".
What simple code is not #
Simple code is not easier to write #
This is an image from a church in Iceland. When you compare it to the church above, it looks a lot simpler, doesn't it? It has less little windows and structures that stick out of it.
But do you think that it is so much easier for an architect to build this and making everything smooth and rounded? The act of making something look simple, certainly requires a lot of time (and practice). So don't assume that writing "simple" code is going to make you write your code faster.
And it requires a certain set of new habits. You have to write your code in a different way. You will start to think of design patterns and method and variable names in a different way. All of this takes time.
And last but not least, this is not going to be a "one time" thing. You will have to revisit your code and refactor it. Move methods around, move abstractions to different places, add abstractions, remove abstractions etc.
The benefits of simpler code #
If writing simple code is not actually faster or easier, what benefits do you gain from doing it like this?
Simple code has less bugs #
Because you have split your code into smaller pieces of abstraction, you will end up having easier to test pieces of code - which leads to less bugs.
Simple code can remove complexity #
When you start moving a highly complex piece of spaghetti code into neat nested functions and patterns, the whole complexity of it can potentially be removed. Simply by naming things differently or moving pieces of code into different little layers of abstraction.
Where to start? #
To actually start writing simple code - you have to know what "simple" looks like. So let's dive into some actual code examples.
This is some (real world) example code. It's coming from an old hobby project that I've worked on, which let you login to a website with your Pokemon Go username and password and then it would give you a profile URL and highscore that you can share with others.
This code is the heart of it all, which is updating the players from the (unofficial) Pokemon Go API.
Lets look at the code:
public function handle()
{
$players = Player::all();
foreach ($players as $player) {
if (!is_null($player->refresh_token)) {
$token = APIHelper::getNewAccessTokenForPlayer($player);
$command = "python pokecli.py -a google -u " . $token;
} else {
$command = "python pokecli.py -a ptc -u " . Crypt::decrypt($player->ptc_username) . " -p ". Crypt::decrypt($player->ptc_password);
}
$process = new Process($command, base_path('bin/pgoapi'));
$process->run();
$output = @json_decode($process->getOutput(), false, 512, JSON_BIGINT_AS_STRING);
$profile = $output->responses->GET_PLAYER->profile;
// Save player
$pokecoins = collect($profile->currency)->where('type', 'POKECOIN')->first();
$stardust = collect($profile->currency)->where('type', 'STARDUST')->first();
$profileData = [
'item_storage' => $profile->item_storage,
'pokecoin' => isset($pokecoins->amount) ? $pokecoins->amount : 0,
'stardust' => isset($stardust->amount) ? $stardust->amount : 0,
'poke_storage' => $profile->poke_storage,
'team' => isset( $profile->team ) ? $profile->team : -1,
];
$player = Player::updateOrCreate([
'username' => $profile->username,
'auth_type' => 'google',
'creation_time' => strftime("%Y-%m-%d %H:%M:%S", ($profile->creation_time/1000))
], $profileData);
\Cache::forget('player_details.'.$player->getKey());
APIHelper::updateStatistics($player, $output);
$this->info('Updated player ' . $player->username);
}
}
Now does that code look simple to you, given the understanding of "simple" that we have now? I don't think it does. There is a lot of stuff going on. And this is not even about giving the code no room to breathe (please give your code spacing).
So how could a "simple" version of this look like? This is what I came up with during my quick 15 minute refactoring. If you want to watch all of it, here you go:
public function handle()
{
Player::query()->each(function (Player $player) {
$output = $this->getLatestPokemonProfileFromApi($player);
$profile = $output->responses->GET_PLAYER->profile;
$player = $this->updatePlayerWithPokemonProfile($profile);
APIHelper::updateStatistics($player, $output);
$this->info('Updated player ' . $player->username);
});
}
Without even going into details about the actual underlying code inside the newly added methods, this code now is simple. You can read it and immediately know what's going on.
Aha, we're getting the latest pokemon profile for this specific player from an API.
Then we update the players profile and finally we update statistics.
Let's look at our definition of simple code:
- expressive
- unsurprising
- transparent
- fun to maintain
I think we have achieved all of this.
Where to go from here? #
The first step to writing simple code is to actually be able to identify simple code. This code looks simple - this code does not.
So congratulations, you just did the first step of writing better and simpler PHP code.
In the upcoming weeks I am going to share more posts and screencasts about how you can write simple code with very practical examples.
I am even working on a new video course about this topic, that I hope to launch early 2020.
If you want to stay in the loop about the course, you can register for the newsletter below: