The slides for my talk at WordCamp Vancouver are available in PDF via the link above. I’ve put my notes together in a decent enough form below to help provide context for each of the slides as they’re pretty content light. Enjoy!
What is Calm and Simple Code
Writing calm and simple code is a great way to craft quality products. Too often are we distracted by deadlines to sit back and really think about what we are learning and what we are providing to end user. The default response to an approaching deadline may be to rush, to look up a bunch of stuff on Google, Stack Exchange, or the WordPress Codex – some prior code or method of doing something, and then just squash it together. There’s a bug here, a bug there. You do the best you can to get them in line and you ship. You’ll go back later and clean it up once you have the time to do it, right?
Maybe. But I’ve taken that approach many times and I’m not sure I can remember one where I really had the time to go back and do it right. There’s always another deadline.
Instead, you can achieve quality and speed by taking the time to understand what you’re doing in the first place. Keep these in mind.
Follow the Action: As you’re coding up a solution, make sure you know what the code you are using is doing. This will help make sure it is the correct solution and ensure you aren’t missing a shorter way to accomplish something.
Be Aware, Act With Intent: Know where you are at all times. Know what your globals are doing. Code with purpose. By maintaining a clean environment around the stuff that you’re doing, you’ll provide a better guarantee for others that they can trust the code you are producing when trying to integrate it with their own.
Readability First: Write your code to be read first, then worry about its functionality. The latter should fall into place if you concentrate on the first. Your future self will be happiest with this approach.
Document Early, Document Often, Document Yourself: And really this doesn’t have as much to do with the documentation in your code as the documentation around it. Try to capture your mindset, your reasoning. Narrate your work.
Follow the Action
The barrier for entry to WordPress development is low.
It’s so approachable that if you’re installing WordPress on your self hosted server, you’ve probably already changed something in the wp-config.php file that has affected the entire code base. Right off the bat, you’re a PHP developer!
Then one day, when you’re trying to figure out how to use a URL shortner like bit.ly automatically with your posts. You search Google for – site:wp.tutsplus.com “functions file” bit.ly – and stumble upon a solution. The greatest thing about this solution that you’ve stumbled on is that it includes code!
So you copy that code and you paste it in to your theme’s functions.php file and bam. You’ve made a change that really affects the codebase. It’s even possible that you’ve brought down your entire site at this point. But, with a little massaging, you find the curly quote that came over in the transition or the undefined variables throwing errors in your dev environment, correct it, and you’re live again.
And Just like that, you’ve achieved another level of development.
It’s actually a really cool feeling. Because of that process being available, it is so easy to get started developing cool projects. And it’s really easy to keep doing those kind of projects. And this is where you have to be careful.
As a ‘WordPress developer’, you can probably stay afloat for a long time just by finding different tutorials, copying the code, and massaging it into place so that it works on your site. There’s not necessarily anything wrong with this. But if you really want this to be a craft, you should treat it like one. You’ll appreciate the code you write and your friends will like you better.
Invest The Time
Going further with anything means being willing to invest the time. Let’s go through an example and read through some core code. It becomes pretty eye opening pretty quickly.
Your goal is to retrieve a complete array of category objects. After some fairly easy searching, let’s say that you land on the Codex page for get_categories().
Now, I’ll stop for a second. The first lesson in this, before I go any further, is to never criticize the Codex. If you’re spending the time to criticize it, that means you haven’t yet spent the time to fix it.
So fix it or hush.
That said, let’s criticize this page on the Codex – we can fix it later… 🙂
What does this function do?
“Returns an array of category objects matching the query parameters.”
And, how about the arguments?
“Arguments are pretty much the same as wp_list_categories”
Pretty much the same. What does that even mean?
In regards to the ‘parent’ argument.
“There is no default for this parameter. [In 2.8.4]”
Is that only in 2.8.4? Before? After?
And from the example code area:
“This is the code used in the build in category page. Code from 3.0.1”
We then present code that doesn’t actually use get_categories(). And it’s from several WordPress versions ago. How is that example code helpful to us at all?
All of that aside, it’s not really that bad. More than likely it’s accurate, but there are some triggers in the text there that indicate the community may have lost track of this one a bit. Probably because the function pretty much just works.
The most important section to us right now is at the bottom of the entry:
“get_categories() is located in wp-includes/category.php”
There are some other (and often easier ways) to get this information. If you’re using an IDE with support for ‘click to definition’ like PHP Storm or Netbeans, you should have a shortcut that brings you right to the file in your library for easy perusal.
If you are working from the command line, you can use ack to search through all files for mentions of a function – in this case you may want to search for ‘function get_categories’ to get the actual declaration.
Right now we’ll use magic and flip to the next slide.
We have get_categories() in front of us and we’re ready to see what it does. Let’s walk through the brief version. Our first lesson – it doesn’t do much.
Only one default argument is implied, and that’s to set the taxonomy to ‘category’. A filter is then applied to the arguments and then what? We get passed over to get_terms().
Wait. So really, there is no such thing as get_categories(). There is, but there isn’t. What we’re really using is a wrapper for get_terms(). And if you follow get_terms() using one of the methods we covered, you see that we actually move files over to wp-includes/taxonomy.php.
That was quick.
So this actually makes sense. 5 years ago, WordPress moved towards multiple taxonomies rather than relying on the built in categories and tags. As part of that move, get_terms() was introduced and get_categories() was no longer needed. This is the kind of history that’s great to run across and to keep in mind while working with the code base. You may find better ways of doing the things you are already familiar with.
Let’s take a look at a few things that happen in get_terms(). I’ve butchered the function in my slide for size, so don’t pay too much attention there.
- Check taxonomy_exists() – is the requested taxonomy, in this case ‘category’, a real taxonomy?
- Set some default arguments. Now we know what really happens when we call that empty function in the first place if we don’t specify our own.
- Check is_taxonomy_hierarchical() – if the taxonomy is hierarchical, different rules will apply in different places when generating the data.
- Apply the ‘get_terms_args’ filter to change any of the arguments that were provided when get_categories() or get_terms() was called.
- _get_term_hierarchy(), is_term_hierarchical(), taxonomy_exists, get_taxonomy(), taxonomy_exists(), wp_cache_get(), apply get_terms filter, wp_parse_id_list()
- Maybe even get_terms again, repeating this whole process!
- Apply ‘terms_clauses’ filter, like_escape(), _get_term_hierarchy(), _get_term_children()
Wow. A lot happens in that little get_categories() call. It’s kind of amazing that the data we need gets to us in the end. There’s so much that could go wrong. Or right.
Without boring you further and spending time with the function line by line, I think we can accept that a lot happens and identify a few learning points.
I’ll start off by admitting that I had never heard of the core function like_escape() until I went through get_categories() in preparation for this talk. And the fact that taxonomy_exists() may get called a dozen times during this process is mind blowing to me. A necessity, yes, but still crazy. We also learn that there are a few places we could hook in – ‘get_terms’, ‘get_terms_args’, ‘terms_clauses’ where arguments and outputs can be changed as things are processed.
The big takeaway is this.
The more time you spend reading core code and looking for why something happens rather than trying to find a quick solution to a problem, the better off you’ll be. You’ll achieve a sort of natural inclination to find the right solution quicker without having to worry about what Google, SE or the Codex say about it.
To level up a little further, you might start looking for the ‘why’.
Why is it that the code is there. Why does it get called this way. Why is that action or filter there and not somewhere else.
There are so many good reasons to ask these questions and a few great ways to explore the answers. I’m going to toss a couple up here and I really encourage you to spend time learning these tools:
- ‘svn blame’ – opens doors into the past
- core.trac.wordpress.org – every discussion, every argument, every commit message and changeset
The code you write is useful only if you understand what the code you write is doing.
Be Aware, Act With Intent
Once you know how to do a few things, it’s really easy to do them all the time.
- Enqueue scripts and stylesheets
- Fetch external data
Sometimes it’s really easy to do them too much.
Be Aware of Your Surroundings
Use the isses
If you haven’t heard of the ‘isses’, and you probably haven’t because I think I just came up with that term, you will love this. Not only can you decide to enqueue a style or a script, but you can – in most cases – decide exactly when you want that style to be enqueued.
- is_404() is_admin() is_archive() is_author() is_category() is_feed()
- is_front_page() is_home() is_month() is_day() is_year() is_page()
- is_paged() is_post_type_archive() is_search() is_single() is_singular()
- is_tag() is_tax() is_taxonomy() is_term() is_year()
So many isses! And any combination of those could be useful to you. With the tools that WordPress provides, there is really no reason why you should be causing an end user to load a script or a stylesheet into their browser when it is not needed for the actual view that they are on.
Know what tools already exist.
It is more common than it needs to be to stumble upon a plugin or a theme that has its own copy of jQuery 1.4.2 loaded. If you’re lucky, it’s taken the time to actually deregister the recent copy of jQuery 1.7.2 before loading itself so that you’re at least getting a ridiculously old version. If you’re unlucky, it loads on top of the existing jQuery, breaking who knows what.
So scratch that. If you’re dealing with a big library, start off with the assumption that there is a better way to handle it.
jQuery 1.7.2 is in WordPress 3.4.2, jQuery UI 1.8.2 is in WordPress 3.4.2
Recent versions of Backbone and Underscore will be in WordPress 3.5
If you’re aware of what code is being loaded around you, you’ll save so much time in the end.
Cache when needed
Define transient: “lasting only a short time, impermanent”
Define WordPress Transient API – “a simple and standardized way of storing cached data in the database temporarily by giving it a custom name and a timeframe after which it will expire and be deleted.”
I stole that from the Codex, by the way. 🙂
So this is perfect when you’re dealing with a bunch of data that changes pretty infrequently. Rather than making a request to an external source or performing a bunch of queries on every page load, you can do this once and store the data as a transient.
set_transient( ‘jf_fancy_complex_data’, $my_fancy_data, 3600 );
Now my fancy data is stored for the next hour. Any time somebody visits this page, it will be pulled from this transient.
$my_fancy_data = get_transient( ‘jf_fancy_complex_data’ );
If you don’t have some kind of object caching installed, this will end up pulling from the database anyway, but at least it will be one value from one key rather than a complex query.
If you do have some kind of object caching installed, this will happen immediately.
And there’s more to it. Apply the lesson of ‘be aware’ to what you’re receiving back from the transient as well. It has an expiration time, so it’s not always going to be there. If you check and it isn’t, generate it again.
if ( ! $my_fancy_data = get_transient( ‘jf_fancy_complex_data’ ) ) $my_fancy_data = generate_my_fancy_data_transient();
All in all, if you are aware of where you’re putting your code and what it is affecting, everything will be much cleaner.
One of my favorite lessons I’ve learned is when I first started working for 10up. I had done a tone of solo work cranking away at a new theme for one of our clients, starting from scratch. A couple months into the project, Zack did my first extensive code review in order to make sure it was ready for the WordPress.com VIP environment.
Now, while I had kept everything organized in a way that made total sense to myself, it made things very tough for someone else to read. If you hadn’t been involved with writing the code from the beginning, it would take a long time to wrap your mind around how it was organized.
My original approach was simple.
I’ll separate all of my functionality into like parts and put them in their own included file so that it doesn’t clutter up the functions file. That’s cool, not a bad idea.
Now. So that I know what’s really going on, I’ll pull all my hooks and filters out into functions.php to have them in a central place. As long as I remember where the functions are, it will be easy to pop around.
But that fell flat quick. Zack made some great suggestions that made a ton of sense as soon as he called them out and I went back and refactored accordingly.
Now, everybody has their own organizational style, so it’s not so horrible that we all do things differently. However, what I learned from that first code review is that I needed to always pay attention to how readable the code is. In some ways that becomes more important than the functionality of the code itself.
Even if you don’t plan on anybody else ever reading your code, keep in mind that you will change. When you look at that code even just a few months from now, things will have slipped.
You become the other person reading your code.
The more obvious you make things to follow, the more relaxed you’ll be when something comes up or a change needs to be made.
The more clever code you use, the more frustrated you’ll be with your former self when you have to go back and fix it.
When I first learned about ternary operators my life changed. I actually went back and refactored a bunch of code to try and ‘simplify’ it. I thought I was doing myself a favor. I realized later that I was spending more brain power than it was worth just to save a line of code.
I wish extract() was called barf() – @jjj
Because that’s exactly what extract() does. Have 10 keys in an array that you don’t want to type out? Use extract()
Trying to remember where the variable $key_name came from every time you look at a section of code? Stop using extract()
It may feel like you’re helping by by shortening that ever so finely tuned PHP file by 10 lines, but it really does make it easier on the world if you take the time to lay things out clearly. As a bonus, if you’re using an IDE, the entire screen won’t be red from possible undefined variables.
Do what you can to get somebody else to read your code. Swap with a friend, reach out online, anything. Tell them to tell you what causes them to pause as they read through it. Make it a live event. Share over a beer. You will take something away from this process and more than likely, they will to.
Document Early, Document Often, Document Yourself
Narrate Your Work
Narrate Your Work is a phrase coined by Dave Winer [@davewiner / Scripting.com] and it comes to mind often when I’m working on anything. It was a really important part of my development timeline when I decided to start working primarily with WordPress. All of a sudden I had this open source community and I no longer had to keep all of this corporate code hidden from the world.
To me, Narrate Your Work means telling a story. Keeping everyone informed about what you’re doing, what your intent is, and how you’re approaching problems.
There are two key parts to doing this.
One, and I mentioned this before, your future self will wonder what the hell you were thinking. And when you do, it would be nice to have some kind of document that gives at least a brief insight.
“people who narrate their work become helpful to the rest of the organization, because the digital trail they leave makes others more efficient.” [HBR article]
And two, you may find help in the strangest places. If you take the effort to make your work public – through streams like Twitter or Facebook or your blog or some other public space – people will notice. And people will help. They’ll see you start talking about an approach you’re going to take to solve a particular problem and they’ll speak up to share their experience or to learn from yours.
“by airing their questions and challenges work narrators open themselves up to good ideas and helpfulness from others, and so become more efficient themselves.” [HBR article]
Take the same approach with your commit messages. You don’t need to rehash the if/else statement that you just wrote – “This commit assigns blah to the variable if this is true” – Instead, you should talk about the intent and any future considerations that one may have. If you’re faced with a situation in which you need to roll back code, you want the latter.
- Follow the Action.
- Be Aware, Act With Intent
- Readability First, Functionality Second
- Narrate Your Work