Any organization where multiple developers cooperate on a regular basis needs some guidelines to assure optimal quality of the end result. Most of these rules applies to freelancers as well.
Follow the official coding guidelines. Always! You may come up with very good arguments for deviating from them, but we need a well defined standard, so use these:
Internal Plugin Directory
Keep an updated internal plugin catalogue. E.g.:
Forms: Gravity Forms
Lazy Loading: BJ Lazy Load
JS and CSS concatenation: Minit
Add a short description of each plugin and how to set them up optimally.
You should also have a list of must-use-plugins and should-probably-use-plugins that you use for every, or almost every, project.
Functionality goes in plugins – presentation in the theme
All theme independent functionality, e.g. custom post types, should be placed in plugins. If certain functionality should never be disabled, put in a mu-plugin. The customer should be able to switch themes at any point without losing neither content nor functionality.
Likewise, all mandatory functionality for a certain theme goes with the theme.
Code for public release
All code, especially plugins, should be created as if it would be released publicly. This does not only give better code quality, but encourages reusability.
Use a private repo
All plugins that isn’t released publicly should be put in a private repo, so all existing customers can update their plugins from the WordPress dashboard when a new version is ready.
Facilitate for caching
Allow for full page caching for an arbitrary length of time by making sure that all GET requests can be cacheable. Load dynamic content with AJAX using POST requests.
Create an environment as close to the production servers as possible with Vagrant. You should also set up arbitrary server configs for testing, to make sure the code is as portable as possible.
Test on multisite
Make sure all themes and plugins works as expected in a multisite environment, as well as a regular installation.
The constant WP_DEBUG should always be set to true on development – and always be set to false on production. If WP_DEBUG causes warnings in development, fix every single issue it complains about. If the warnings comes from a 3rd party plugin, you should either consider alternatives or fix the code and submit a patch.
Enqueue JS and CSS properly
Use the wp_enqueue_script and wp_enqueue_style functions for every single JS or CSS file you use. No excuses!
All strings should be translateable
Use the gettext functions for every single string.
Provide descriptions for translators
E.g. the word «post» can be both a verb and a noun, which in a different language can have two very different words. Provide descriptions for translators so they know what they are translating.
Create flexible themes
Just because your client have three products they want to showcase on their front page right now, doesn’t mean they don’t have four or two in six months. Make sure your theme doesn’t dictate the content, but is flexible enough to handle added or removed sections.
Keep the templates clean
At some point you’ll find yourself in a situation where you need to add logic to your template files. Make sure the templates are as clean and readable as possible by putting the logic in functions – not defined within the template file itself, but in functions.php or similar.
Write to the correct folder
Even though /wp-content/uploads is the default folder that plugins should write any file to, WordPress can be configured to use a different folder. Make sure you write to the correct folder by using wp_upload_dir()
If your theme or plugin use a custom image size just for specific pieces of content, like a custom post type for products, you don’t need to fill up the disk space with unnecessary created images with add_image_size(). Use WP_Image_Editor to create custom sized images of just the ones you need – and cache the result.
Never rely on a resource being there
Never expect a resource – whether it’s an external API or an internal file – to be available. It might go offline, be deleted or moved. Always do checks first. If possible, recreate the resource. If not, work around it.
Cache at every level
Of course you cache results from external APIs, but you should also make sure you cache results from the database and generated output. All these caches are made on the server. But also make sure you cache results at the client level. The HTML5 API localStorage is excellent for this purpose.
Say you have a full page cache that only expires once a day, but you are displaying an Instagram widget on every page. The Instagram widget can be pulled in via AJAX, but the client doesn’t have to make that extra request on every page load. It can cache the widget in localStorage and only refresh it from the web server every 15 minutes or so.
Retry, not hammer
If your cache has expired and the external API is unavailable, don’t retry for every incoming request you got. Hammering doesn’t help neither you, your users nor the external API provider. Only retry at a set interval.
Tailor the admin to your client’s needs
Most of the time, the wise choice is to not give your client an administrator level account. You should rather give them an editor level account and tailor what that user level has access to and see in the WordPress admin interface.
E.g. if the client only needs to update pages, that is all they need to see in the backend.
Always agree with the client before you remove options for them, though. It might be a good idea to also give them an administrator level account (not named «admin» of course) that they don’t use on a regular basis, but keep for safety it they should need it in the future.
Don’t comment your code
– Wait, what?
– OK, let me rephrase that: Write your code so it doesn’t have to be commented.
Make sure everything is neatly structured. Use names for variables, classes and functions that is self-describing. Functions should do as little as possible and be as short as possible.
Be protocol agnostic
Just as you should make no assumptions to where in the structure WordPress itself is installed or where the upload directory might be located, you should make sure everything works fine regarding of what protocol is used – or port for that matter.
Create – and use – unit tests
Yes, it might be tedious and sometimes take more time than write the code itself, but in the long run it’s worth it. As the complexity of your project grows, unit tests will actually save you time. And after all, chasing bugs isn’t fun either.
Never use temporary solutions
There is nothing as permanent as a temporary fix. Avoid hacks at all cost.
Credits & Thanks
Thanks to Drew Jaynes for pointing out that WordPress has inline documentation standards that supersede the phpDocumenter spec.