Code quality is a debated subject. What is ‘good code’? How can we recognize good code? Is there a handbook on what good code should look like?
I participated recently in a twitter debate that touched on the subject of coding styles
Procedural vs object-oriented (OOP) programming. What is better? It turns out that there is no real right answer. It all depends.
I’m all for avoiding unnecessary complexities in your code. If you can write your plugin in 50 lines of procedural PHP go for it. Adding 5 classes and a lot of boilerplate code won’t make it better. There is no magic that makes OOP the silver bullet for your project.
If your project is very complex, then it makes more sense to think about architecture. How your code interacts with one another. Does it make sense to split your codebase by functionality or by business domains? Like in the case of domain-driven design (DDD).
These are all valid questions that a professional developer will face in their lifetime.
One thing is certain for any approach you might take. You need to be mindful of the code quality.
What is code quality?
It’s not easy to define a good code. Each of us has their own interpretation of what good code is. Quite like saying which coding style is better. We could say that good code is the one that does its job. When we dig deeper, we see that it’s not only about getting things done.
Is the code that gets things done safely? Will it cause more damage to the client, than the problem it solved? Is it easy to change? How does it fare as the project lives on?
Once we start answering these questions we start to scratch the surface of the meaning of the word good code.
A code quality would be a measure of the ‘goodness’ of your code.
There are some identifiers we could use to see if the code is of good quality.
Are certain coding standards used? Is it easy to read? Is it documented well? Is it testable? Can we refactor it without fear? Is it safe?
We think these things are only a concern for a developer. But it is a business owner’s concern. Security especially. Insecure code costs a lot. And a code that is hard to maintain even more. It’s estimated that the project maintenance typically consumes 40 to 80 percent of software costs, as Robert Glass et. al. mention in his book.
Code smells
When you review code for a long time, you get to notice certain things that look odd. Sometimes you cannot explain at once why the code is not good. But your gut is telling you something is not right.
What you encountered is a code smell. They are not necessary bugs. But they indicate that there are some design issues that may cause problems in the future.
One such code smell could be a tight coupling problem between classes. I’ve described that in the Keeping up with the dependencies article.
Another smell would be a deeply nested code:
$response = wp_remote_get('https://example.com');
if (!is_wp_error($response)) {
if (!empty(wp_remote_retrieve_body($response))) {
if (...) // And so on with many branches.
} else {
// More code here.
}
}
Code language: PHP (php)
This is usually solved by using guard clauses and early returns:
$response = wp_remote_get('https://example.com');
if (is_wp_error($response)) {
return $response;
}
$body = wp_remote_retrieve_body($response);
if (empty($body)) {
return new WP_Error(204, esc_html__('The response body is empty :(', 'textdomain'));
}
// Continue writing the code.
Code language: PHP (php)
Unreadable code is also a code smell. Developers are the ones who read the code. Not computers. Computers don’t care. But if you are making your code unreadable you are making yourself a disservice. In six months you won’t be able to read the code, let alone another developer who has to take over after you.
When writing complex business logic it’s best to follow the principles of test-driven development (TDD).
Write tests, which will fail. Then write the logic that will make the test pass. Then refactor the code. Having tests is like having a safety net. You can make your code readable when you know you won’t break anything.
Following certain coding standards is also a great way to ensure readability and code quality.
What can you do to improve code quality?
As you may or may not know, I love automation. Why burden yourself with things that computers can do better than you?
In part that’s the reason, developers choose this profession. We are inherently lazy (in a good way).
These days there are tons of tools you can use that will help improve the quality of your code. For JS there are ESLint and Prettier.
For PHP we have a multitude of tools at our disposal: PHP_CodeSniffer, PHPStan, Psalm, or Rector to name a few.
I use PHPCS and PHPStan in my projects, so I’ll describe how you can set those up in your WordPress project.
Setting up PHPCS and PHPStan in your WordPress project
The example will follow my recent refactor of Woo Solo Api plugin.
You’ll have to use Composer to set these tools up. You could set up PHPCS without Composer, but we want to simplify things.
In your composer.json
file (either in the theme or plugin root), you’d add
{
"name": "plugin-vendor/plugin-name",
"type": "wordpress-plugin",
"description": "Super cool plugin.",
"keywords": ["plugin", "WordPress"],
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.7",
"php-stubs/woocommerce-stubs": "^4.6",
"php-stubs/wordpress-globals": "^0.2.0",
"phpcompatibility/php-compatibility": "^9.3",
"phpcompatibility/phpcompatibility-wp": "^2.1",
"squizlabs/php_codesniffer": "^3.5.6",
"szepeviktor/phpstan-wordpress": "^0.6.5",
"roave/security-advisories": "dev-master"
},
"autoload": {
"psr-4": {
"Plugin\\": "src"
}
},
"config": {
"sort-packages": true,
"optimize-autoloader": true,
"process-timeout": 2000
},
"scripts": {
"standards:check": "@php ./vendor/squizlabs/php_codesniffer/bin/phpcs",
"standards:fix": "@php ./vendor/squizlabs/php_codesniffer/bin/phpcbf",
"analyze": "@php ./vendor/bin/phpstan analyze"
}
}
Code language: JSON / JSON with Comments (json)
Running composer install
would install the necessary packages.
To run PHPCS and PHPStan, you’ll need to set up their configuration files. In the case of PHPCS, you should have .phpcs.xml.dist
, and for PHPStan you’ll need phpstan.neon
.
<?xml version="1.0"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Your Plugin Name" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/squizlabs/PHP_CodeSniffer/master/phpcs.xsd">
<description>The Coding standard for your plugin.</description>
<file>.</file>
<arg value="sp"/>
<arg name="extensions" value="php"/>
<arg name="parallel" value="8"/>
<arg name="basepath" value="./"/>
<arg name="tab-width" value="4"/>
<!-- Exclude directories. -->
<exclude-pattern>*/vendor/*</exclude-pattern>
<!-- Enforce PSR12 ruleset. -->
<rule ref="PSR12"/>
<!-- Check code for cross-version PHP compatibility. -->
<config name="testVersion" value="7.2-"/>
<rule ref="PHPCompatibility"/>
<rule ref="PHPCompatibilityWP">
<include-pattern>*\.(php|inc)$</include-pattern>
</rule>
<!-- Check against minimum WP version. -->
<config name="minimum_supported_wp_version" value="5.2"/>
</ruleset>
Code language: HTML, XML (xml)
This is an example of the .phpcs.xml.dist
file. You can check the annotated ruleset example for phpcs for more details.
PHPCS works kinda like JS linters. It will sniff your code for certain violations. And you can apply various coding standards. Like WordPress Coding Standards.
Explaining how it works in depth is a good topic for a separate article.
PHPStan is another static analysis tool that will go a bit more in-depth than PHPCS. It will check for things like type mismatch, race conditions, but also unused methods or function calls, etc. . PHPCS is a script that tokenizes your code, and based on standards will try to detect errors in it. PHPStan uses autoloading and using reflection will try to understand your code. For instance, if you implemented an interface but didn’t implement a method from the interface. That would throw an error.
An example of PHPStan config would look like:
includes:
- vendor/phpstan/phpstan/conf/bleedingEdge.neon
- vendor/szepeviktor/phpstan-wordpress/extension.neon
parameters:
level: max
inferPrivatePropertyTypeFromConstructor: true
checkMissingIterableValueType: false
paths:
- src/
bootstrapFiles:
- vendor/php-stubs/woocommerce-stubs/woocommerce-stubs.php
- vendor/php-stubs/woocommerce-stubs/woocommerce-packages-stubs.php
- vendor/php-stubs/wordpress-globals/source/wp-admin/includes/file.php
Code language: JavaScript (javascript)
You can read more about it in the official documentation.
These days it’s popular to automate everything, so you can even automate these checks by using Travis CI or Github Actions in your CI/CD pipeline. I’ve even created a GH action for running WordPress PHPStan, which you can check out here.
Conclusion
You should worry about your code quality. Having a maintainable, testable, and clean code will save you time (and money) in the long run. Luckily there are tools that we can use that can help us maintain good code quality.
Every professional developer and engineer should be mindful of it and include it as a first step when starting a project. It’s one of these things that you just do. You don’t think about it. Using tools to automate this process will win us half the battle in writing better code.
The icon on the featured image made by Freepik from www.flaticon.com.
Leave a Reply